[jboss-cvs] JBossBlog SVN: r350 - in feeds100P26: docs and 125 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Thu Mar 26 09:55:24 EDT 2009


Author: sviluppatorefico
Date: 2009-03-26 09:55:24 -0400 (Thu, 26 Mar 2009)
New Revision: 350

Added:
   feeds100P26/blog.iml
   feeds100P26/build-design.properties
   feeds100P26/build-dev.properties
   feeds100P26/build-prod.properties
   feeds100P26/build.properties
   feeds100P26/build.xml
   feeds100P26/docs/
   feeds100P26/docs/admin_link.png
   feeds100P26/docs/group_header.png
   feeds100P26/docs/tutorial.html
   feeds100P26/lib/
   feeds100P26/lib/activation.jar
   feeds100P26/lib/ant-antlr.jar
   feeds100P26/lib/antlr-runtime.jar
   feeds100P26/lib/antlr.jar
   feeds100P26/lib/asm-attrs.jar
   feeds100P26/lib/asm.jar
   feeds100P26/lib/cglib-nodep.jar
   feeds100P26/lib/cglib.jar
   feeds100P26/lib/commons-beanutils.jar
   feeds100P26/lib/commons-collections.jar
   feeds100P26/lib/commons-configuration-1.6.jar
   feeds100P26/lib/commons-digester.jar
   feeds100P26/lib/commons-lang-2.4.jar
   feeds100P26/lib/commons-lang.jar
   feeds100P26/lib/commons-logging.jar
   feeds100P26/lib/core.jar
   feeds100P26/lib/dbunit.jar
   feeds100P26/lib/dom4j.jar
   feeds100P26/lib/drools-compiler.jar
   feeds100P26/lib/drools-core.jar
   feeds100P26/lib/easymock.jar
   feeds100P26/lib/ehcache-1.2.3.jar
   feeds100P26/lib/ejb-api.jar
   feeds100P26/lib/el-api.jar
   feeds100P26/lib/groovy-all.jar
   feeds100P26/lib/gwt-servlet.jar
   feeds100P26/lib/hibernate-annotations.jar
   feeds100P26/lib/hibernate-commons-annotations.jar
   feeds100P26/lib/hibernate-entitymanager.jar
   feeds100P26/lib/hibernate-search.jar
   feeds100P26/lib/hibernate-validator.jar
   feeds100P26/lib/hibernate.jar
   feeds100P26/lib/hibernate3.jar
   feeds100P26/lib/htmlcleaner1_6.jar
   feeds100P26/lib/itext.jar
   feeds100P26/lib/janino.jar
   feeds100P26/lib/javassist.jar
   feeds100P26/lib/jaxb-api.jar
   feeds100P26/lib/jaxws-api.jar
   feeds100P26/lib/jboss-aop.jar
   feeds100P26/lib/jboss-cache.jar
   feeds100P26/lib/jboss-common-core.jar
   feeds100P26/lib/jboss-dependency.jar
   feeds100P26/lib/jboss-deployers-client-spi.jar
   feeds100P26/lib/jboss-deployers-core-spi.jar
   feeds100P26/lib/jboss-el.jar
   feeds100P26/lib/jboss-embedded-api.jar
   feeds100P26/lib/jboss-jmx.jar
   feeds100P26/lib/jboss-kernel.jar
   feeds100P26/lib/jboss-logging-spi.jar
   feeds100P26/lib/jboss-seam-debug.jar
   feeds100P26/lib/jboss-seam-gen.jar
   feeds100P26/lib/jboss-seam-ioc.jar
   feeds100P26/lib/jboss-seam-mail.jar
   feeds100P26/lib/jboss-seam-pdf.jar
   feeds100P26/lib/jboss-seam-remoting.jar
   feeds100P26/lib/jboss-seam-ui.jar
   feeds100P26/lib/jboss-seam.jar
   feeds100P26/lib/jboss-system.jar
   feeds100P26/lib/jbossxb.jar
   feeds100P26/lib/jbpm-jpdl.jar
   feeds100P26/lib/jcommon.jar
   feeds100P26/lib/jdom.jar
   feeds100P26/lib/jericho-html-2.5.jar
   feeds100P26/lib/jfreechart.jar
   feeds100P26/lib/jgroups.jar
   feeds100P26/lib/jms.jar
   feeds100P26/lib/jsf-api.jar
   feeds100P26/lib/jsf-facelets.jar
   feeds100P26/lib/jsf-impl.jar
   feeds100P26/lib/jsp-api.jar
   feeds100P26/lib/jsr173_api.jar
   feeds100P26/lib/jsr181-api.jar
   feeds100P26/lib/jsr250-api.jar
   feeds100P26/lib/jstl.jar
   feeds100P26/lib/jta.jar
   feeds100P26/lib/let-tag.jar
   feeds100P26/lib/log4j.jar
   feeds100P26/lib/lucene-core.jar
   feeds100P26/lib/mail.jar
   feeds100P26/lib/meldware-mailapi.jar
   feeds100P26/lib/meldware-mailjmx.jar
   feeds100P26/lib/mvel14.jar
   feeds100P26/lib/mysql.jar
   feeds100P26/lib/persistence-api.jar
   feeds100P26/lib/portal-common-lib.jar
   feeds100P26/lib/portal-identity-lib.jar
   feeds100P26/lib/portal-portlet-jsr168api-lib.jar
   feeds100P26/lib/portlet-api.jar
   feeds100P26/lib/richfaces-api.jar
   feeds100P26/lib/richfaces-impl.jar
   feeds100P26/lib/richfaces-ui.jar
   feeds100P26/lib/rome-1.0RC1.jar
   feeds100P26/lib/saaj-api.jar
   feeds100P26/lib/servlet-api.jar
   feeds100P26/lib/shotoku-base.jar
   feeds100P26/lib/spring.jar
   feeds100P26/lib/src/
   feeds100P26/lib/src/jboss-seam-debug-sources.jar
   feeds100P26/lib/src/jboss-seam-gen-sources.jar
   feeds100P26/lib/src/jboss-seam-ioc-sources.jar
   feeds100P26/lib/src/jboss-seam-mail-sources.jar
   feeds100P26/lib/src/jboss-seam-pdf-sources.jar
   feeds100P26/lib/src/jboss-seam-remoting-sources.jar
   feeds100P26/lib/src/jboss-seam-sources.jar
   feeds100P26/lib/src/jboss-seam-ui-sources.jar
   feeds100P26/lib/standard.jar
   feeds100P26/lib/test/
   feeds100P26/lib/test/hibernate-all.jar
   feeds100P26/lib/test/jboss-embedded-all.jar
   feeds100P26/lib/test/thirdparty-all.jar
   feeds100P26/lib/testng.jar
   feeds100P26/lib/urlrewritefilter.jar
   feeds100P26/lib/velocity-1.5.jar
   feeds100P26/lib/velocity-dep-1.5.jar
   feeds100P26/resources-portlet/
   feeds100P26/resources-portlet/WEB-INF/
   feeds100P26/resources-portlet/WEB-INF/blog-object.xml
   feeds100P26/resources-portlet/WEB-INF/jboss-app.xml
   feeds100P26/resources-portlet/WEB-INF/jboss-portlet.xml
   feeds100P26/resources-portlet/WEB-INF/portlet-instances.xml
   feeds100P26/resources-portlet/WEB-INF/portlet.xml
   feeds100P26/resources-portlet/WEB-INF/web.xml
   feeds100P26/resources/
   feeds100P26/resources/META-INF/
   feeds100P26/resources/META-INF/application.xml
   feeds100P26/resources/META-INF/ejb-jar.xml
   feeds100P26/resources/META-INF/jboss-app.xml
   feeds100P26/resources/META-INF/jbossblog.taglib.xml
   feeds100P26/resources/META-INF/persistence-design.xml
   feeds100P26/resources/META-INF/persistence-dev.xml
   feeds100P26/resources/META-INF/persistence-prod.xml
   feeds100P26/resources/META-INF/security.drl
   feeds100P26/resources/WEB-INF/
   feeds100P26/resources/WEB-INF/components.xml
   feeds100P26/resources/WEB-INF/faces-config.xml
   feeds100P26/resources/WEB-INF/pages.xml
   feeds100P26/resources/WEB-INF/urlrewrite.xml
   feeds100P26/resources/WEB-INF/web-design.xml
   feeds100P26/resources/WEB-INF/web-dev.xml
   feeds100P26/resources/WEB-INF/web-prod.xml
   feeds100P26/resources/blog-design-ds.xml
   feeds100P26/resources/blog-dev-ds.xml
   feeds100P26/resources/blog-ehcache.xml
   feeds100P26/resources/blog-prod-ds.xml
   feeds100P26/resources/components.properties
   feeds100P26/resources/messages_en.properties
   feeds100P26/resources/seam.properties
   feeds100P26/resources/templates/
   feeds100P26/resources/templates/atom_standard.vm
   feeds100P26/resources/templates/rss1_standard.vm
   feeds100P26/resources/templates/rss2_standard.vm
   feeds100P26/resources/velocity.properties
   feeds100P26/src/
   feeds100P26/src/action/
   feeds100P26/src/action/org/
   feeds100P26/src/action/org/jboss/
   feeds100P26/src/action/org/jboss/blog/
   feeds100P26/src/action/org/jboss/blog/servlet/
   feeds100P26/src/action/org/jboss/blog/servlet/FeedsServlet.java
   feeds100P26/src/action/org/jboss/blog/session/
   feeds100P26/src/action/org/jboss/blog/session/cache/
   feeds100P26/src/action/org/jboss/blog/session/cache/CacheManager.java
   feeds100P26/src/action/org/jboss/blog/session/cache/CacheManagerHashMapImpl.java
   feeds100P26/src/action/org/jboss/blog/session/cache/FeedsChangesObserver.java
   feeds100P26/src/action/org/jboss/blog/session/category/
   feeds100P26/src/action/org/jboss/blog/session/category/CategoryServiceBean.java
   feeds100P26/src/action/org/jboss/blog/session/configuration/
   feeds100P26/src/action/org/jboss/blog/session/configuration/ConfigurationManager.java
   feeds100P26/src/action/org/jboss/blog/session/converter/
   feeds100P26/src/action/org/jboss/blog/session/converter/FeedConverter.java
   feeds100P26/src/action/org/jboss/blog/session/converter/GroupConverter.java
   feeds100P26/src/action/org/jboss/blog/session/converter/PostConverter.java
   feeds100P26/src/action/org/jboss/blog/session/converter/TemplateConverter.java
   feeds100P26/src/action/org/jboss/blog/session/exceptions/
   feeds100P26/src/action/org/jboss/blog/session/exceptions/FeedNotFoundRuntimeException.java
   feeds100P26/src/action/org/jboss/blog/session/exceptions/PostNotFoundRuntimeException.java
   feeds100P26/src/action/org/jboss/blog/session/feed/
   feeds100P26/src/action/org/jboss/blog/session/feed/FeedsServiceImpl.java
   feeds100P26/src/action/org/jboss/blog/session/feed/InvalidFeedTypeException.java
   feeds100P26/src/action/org/jboss/blog/session/feed/dao/
   feeds100P26/src/action/org/jboss/blog/session/feed/dao/AggregatedFeedDao.java
   feeds100P26/src/action/org/jboss/blog/session/feed/dao/FeedDao.java
   feeds100P26/src/action/org/jboss/blog/session/feed/dao/HighlightsFeedDao.java
   feeds100P26/src/action/org/jboss/blog/session/feed/dao/IndividualPostsFeedDao.java
   feeds100P26/src/action/org/jboss/blog/session/feed/dao/RemoteFeedDao.java
   feeds100P26/src/action/org/jboss/blog/session/feed/lock/
   feeds100P26/src/action/org/jboss/blog/session/feed/lock/FeedsLocksBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedDeleteListener.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedFeedModBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/FeedModBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/FilterAddBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsDeleteListener.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsFeedModBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/IndividualFeedModBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/PostsValidator.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsListener.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsToolsBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/mod/RemoteFeedModBean.java
   feeds100P26/src/action/org/jboss/blog/session/feed/posts/
   feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedPosts.java
   feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedStack.java
   feeds100P26/src/action/org/jboss/blog/session/feed/posts/DatabaseFeedPosts.java
   feeds100P26/src/action/org/jboss/blog/session/feed/type/
   feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedType.java
   feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedTypes.java
   feeds100P26/src/action/org/jboss/blog/session/feed/update/
   feeds100P26/src/action/org/jboss/blog/session/feed/update/IndividualPostsFeedUpdate.java
   feeds100P26/src/action/org/jboss/blog/session/feed/update/RemoteFeedUpdate.java
   feeds100P26/src/action/org/jboss/blog/session/group/
   feeds100P26/src/action/org/jboss/blog/session/group/GroupModBean.java
   feeds100P26/src/action/org/jboss/blog/session/group/GroupsServiceImpl.java
   feeds100P26/src/action/org/jboss/blog/session/merge/
   feeds100P26/src/action/org/jboss/blog/session/merge/FeedsServicePostsIterator.java
   feeds100P26/src/action/org/jboss/blog/session/merge/ListPostsIterator.java
   feeds100P26/src/action/org/jboss/blog/session/merge/MergeServiceBean.java
   feeds100P26/src/action/org/jboss/blog/session/merge/PostManager.java
   feeds100P26/src/action/org/jboss/blog/session/merge/PostsIterator.java
   feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdService.java
   feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdServiceBean.java
   feeds100P26/src/action/org/jboss/blog/session/parser/
   feeds100P26/src/action/org/jboss/blog/session/parser/ParserException.java
   feeds100P26/src/action/org/jboss/blog/session/parser/ParserService.java
   feeds100P26/src/action/org/jboss/blog/session/parser/ParserServiceImpl.java
   feeds100P26/src/action/org/jboss/blog/session/scanner/
   feeds100P26/src/action/org/jboss/blog/session/scanner/AnnotationScanner.java
   feeds100P26/src/action/org/jboss/blog/session/scanner/ClassHandler.java
   feeds100P26/src/action/org/jboss/blog/session/scanner/Init.java
   feeds100P26/src/action/org/jboss/blog/session/search/
   feeds100P26/src/action/org/jboss/blog/session/search/PostSearchBean.java
   feeds100P26/src/action/org/jboss/blog/session/search/SearchReindexObserver.java
   feeds100P26/src/action/org/jboss/blog/session/security/
   feeds100P26/src/action/org/jboss/blog/session/security/Authenticator.java
   feeds100P26/src/action/org/jboss/blog/session/security/FeedsCombinedRole.java
   feeds100P26/src/action/org/jboss/blog/session/security/FeedsIdentity.java
   feeds100P26/src/action/org/jboss/blog/session/security/InvalidLoginException.java
   feeds100P26/src/action/org/jboss/blog/session/security/RestrictedKeyGenerator.java
   feeds100P26/src/action/org/jboss/blog/session/security/SecurityGroupConverter.java
   feeds100P26/src/action/org/jboss/blog/session/security/SecurityModBean.java
   feeds100P26/src/action/org/jboss/blog/session/security/SecurityObserver.java
   feeds100P26/src/action/org/jboss/blog/session/security/SecurityRoleConverter.java
   feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserConverter.java
   feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserKeys.java
   feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserSelectBean.java
   feeds100P26/src/action/org/jboss/blog/session/security/external/
   feeds100P26/src/action/org/jboss/blog/session/security/external/AbstractExternalSecurityService.java
   feeds100P26/src/action/org/jboss/blog/session/security/external/DummyExternalSecurityService.java
   feeds100P26/src/action/org/jboss/blog/session/security/external/ExternalSecurityService.java
   feeds100P26/src/action/org/jboss/blog/session/security/filtering/
   feeds100P26/src/action/org/jboss/blog/session/security/filtering/FeedsSecurity.java
   feeds100P26/src/action/org/jboss/blog/session/security/filtering/GroupsSecurity.java
   feeds100P26/src/action/org/jboss/blog/session/security/filtering/HighlightsSecurity.java
   feeds100P26/src/action/org/jboss/blog/session/security/tools/
   feeds100P26/src/action/org/jboss/blog/session/security/tools/FeedSecurityTools.java
   feeds100P26/src/action/org/jboss/blog/session/tools/
   feeds100P26/src/action/org/jboss/blog/session/tools/AdminBean.java
   feeds100P26/src/action/org/jboss/blog/session/tools/CaptchaToolsBean.java
   feeds100P26/src/action/org/jboss/blog/session/tools/PostToToolsBean.java
   feeds100P26/src/action/org/jboss/blog/session/tools/StringToolsBean.java
   feeds100P26/src/action/org/jboss/blog/session/update/
   feeds100P26/src/action/org/jboss/blog/session/update/UpdateException.java
   feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandler.java
   feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsync.java
   feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsyncImpl.java
   feeds100P26/src/action/org/jboss/blog/session/update/UpdateManager.java
   feeds100P26/src/action/org/jboss/blog/session/update/UpdateThread.java
   feeds100P26/src/action/org/jboss/blog/session/validator/
   feeds100P26/src/action/org/jboss/blog/session/validator/UniqueFeedNameValidator.java
   feeds100P26/src/action/org/jboss/blog/session/validator/UniqueGroupNameValidator.java
   feeds100P26/src/action/org/jboss/blog/session/validator/UniqueTemplateNameValidator.java
   feeds100P26/src/action/org/jboss/blog/session/view/
   feeds100P26/src/action/org/jboss/blog/session/view/FeedViewBean.java
   feeds100P26/src/action/org/jboss/blog/session/view/LinkServiceImpl.java
   feeds100P26/src/action/org/jboss/blog/session/view/PostViewBean.java
   feeds100P26/src/action/org/jboss/blog/session/xml/
   feeds100P26/src/action/org/jboss/blog/session/xml/XmlService.java
   feeds100P26/src/action/org/jboss/blog/session/xml/content/
   feeds100P26/src/action/org/jboss/blog/session/xml/content/ContentResponse.java
   feeds100P26/src/action/org/jboss/blog/session/xml/content/InMemoryContentResponse.java
   feeds100P26/src/action/org/jboss/blog/session/xml/content/ServletResponseContentResponse.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/DatabaseResourceLoader.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/InvalidTemplateTypeException.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateBootstrap.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateModBean.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateServiceBean.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/VelocityXmlService.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/AtomXmlTools.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss1XmlTools.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss2XmlTools.java
   feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/XmlTools.java
   feeds100P26/src/action/org/jboss/blog/tools/
   feeds100P26/src/action/org/jboss/blog/tools/PostFilterTools.java
   feeds100P26/src/action/org/jboss/shotoku/
   feeds100P26/src/action/org/jboss/shotoku/web/
   feeds100P26/src/action/org/jboss/shotoku/web/FilesystemResourceResolver.java
   feeds100P26/src/action/org/jboss/shotoku/web/ResourcesFilter.java
   feeds100P26/src/model/
   feeds100P26/src/model/org/
   feeds100P26/src/model/org/jboss/
   feeds100P26/src/model/org/jboss/blog/
   feeds100P26/src/model/org/jboss/blog/model/
   feeds100P26/src/model/org/jboss/blog/model/Category.java
   feeds100P26/src/model/org/jboss/blog/model/Enclosure.java
   feeds100P26/src/model/org/jboss/blog/model/Group.java
   feeds100P26/src/model/org/jboss/blog/model/Image.java
   feeds100P26/src/model/org/jboss/blog/model/Post.java
   feeds100P26/src/model/org/jboss/blog/model/RestrictedCategory.java
   feeds100P26/src/model/org/jboss/blog/model/RestrictedEnclosure.java
   feeds100P26/src/model/org/jboss/blog/model/RestrictedImage.java
   feeds100P26/src/model/org/jboss/blog/model/RestrictedPost.java
   feeds100P26/src/model/org/jboss/blog/model/Template.java
   feeds100P26/src/model/org/jboss/blog/model/XmlType.java
   feeds100P26/src/model/org/jboss/blog/model/configuration/
   feeds100P26/src/model/org/jboss/blog/model/configuration/Configuration.java
   feeds100P26/src/model/org/jboss/blog/model/feed/
   feeds100P26/src/model/org/jboss/blog/model/feed/AggregatedFeed.java
   feeds100P26/src/model/org/jboss/blog/model/feed/Feed.java
   feeds100P26/src/model/org/jboss/blog/model/feed/HighlightsFeed.java
   feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostInfo.java
   feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostsFeed.java
   feeds100P26/src/model/org/jboss/blog/model/feed/PostAuthorType.java
   feeds100P26/src/model/org/jboss/blog/model/feed/RemoteFeed.java
   feeds100P26/src/model/org/jboss/blog/model/feed/RestrictedFeed.java
   feeds100P26/src/model/org/jboss/blog/model/log/
   feeds100P26/src/model/org/jboss/blog/model/log/PropositionsLog.java
   feeds100P26/src/model/org/jboss/blog/model/post/
   feeds100P26/src/model/org/jboss/blog/model/post/PostFilter.java
   feeds100P26/src/model/org/jboss/blog/model/post/filter/
   feeds100P26/src/model/org/jboss/blog/model/post/filter/AbstractRegexpFilter.java
   feeds100P26/src/model/org/jboss/blog/model/post/filter/AndFilter.java
   feeds100P26/src/model/org/jboss/blog/model/post/filter/AuthorRegexpFilter.java
   feeds100P26/src/model/org/jboss/blog/model/post/filter/CategoryRegexpFilter.java
   feeds100P26/src/model/org/jboss/blog/model/post/filter/NotPodcastFilter.java
   feeds100P26/src/model/org/jboss/blog/model/post/filter/PodcastFilter.java
   feeds100P26/src/model/org/jboss/blog/model/post/filter/TotalFilter.java
   feeds100P26/src/model/org/jboss/blog/model/security/
   feeds100P26/src/model/org/jboss/blog/model/security/FeedsSecurityRole.java
   feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityGroup.java
   feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityUser.java
   feeds100P26/src/model/org/jboss/blog/model/security/SecurityGroup.java
   feeds100P26/src/model/org/jboss/blog/model/security/SecurityMapping.java
   feeds100P26/src/model/org/jboss/blog/model/security/SecurityUser.java
   feeds100P26/src/portal/
   feeds100P26/src/portal/org/
   feeds100P26/src/portal/org/jboss/
   feeds100P26/src/portal/org/jboss/blog/
   feeds100P26/src/portal/org/jboss/blog/portlet/
   feeds100P26/src/portal/org/jboss/blog/portlet/BlogPortlet.java
   feeds100P26/src/portal/org/jboss/blog/session/
   feeds100P26/src/portal/org/jboss/blog/session/security/
   feeds100P26/src/portal/org/jboss/blog/session/security/external/
   feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalExternalSecurityService.java
   feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalRole.java
   feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityException.java
   feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityService.java
   feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityServiceImpl.java
   feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalUser.java
   feeds100P26/src/portal/org/jboss/blog/session/security/external/SecurityBootstrap.java
   feeds100P26/src/services/
   feeds100P26/src/services/org/
   feeds100P26/src/services/org/jboss/
   feeds100P26/src/services/org/jboss/blog/
   feeds100P26/src/services/org/jboss/blog/service/
   feeds100P26/src/services/org/jboss/blog/service/FeedNotFoundException.java
   feeds100P26/src/services/org/jboss/blog/service/FeedsService.java
   feeds100P26/src/services/org/jboss/blog/service/GroupsService.java
   feeds100P26/src/services/org/jboss/blog/service/LinkService.java
   feeds100P26/src/services/org/jboss/blog/service/PostNotFoundException.java
   feeds100P26/src/shotoku/
   feeds100P26/src/shotoku/org/
   feeds100P26/src/shotoku/org/jboss/
   feeds100P26/src/shotoku/org/jboss/blog/
   feeds100P26/src/shotoku/org/jboss/blog/model/
   feeds100P26/src/shotoku/org/jboss/blog/model/shotoku/
   feeds100P26/src/shotoku/org/jboss/blog/model/shotoku/ShotokuFeed.java
   feeds100P26/src/shotoku/org/jboss/blog/session/
   feeds100P26/src/shotoku/org/jboss/blog/session/feed/
   feeds100P26/src/shotoku/org/jboss/blog/session/feed/dao/
   feeds100P26/src/shotoku/org/jboss/blog/session/feed/dao/ShotokuFeedDao.java
   feeds100P26/src/shotoku/org/jboss/blog/session/feed/mod/
   feeds100P26/src/shotoku/org/jboss/blog/session/feed/mod/ShotokuModBean.java
   feeds100P26/src/shotoku/org/jboss/blog/session/feed/update/
   feeds100P26/src/shotoku/org/jboss/blog/session/feed/update/ShotokuFeedUpdate.java
   feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/
   feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/PostContentTooLargeException.java
   feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuException.java
   feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuFeedService.java
   feeds100P26/src/shotoku/org/jboss/shotoku/
   feeds100P26/src/shotoku/org/jboss/shotoku/web/
   feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuFilesystemResourceResolver.java
   feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuResourcesFilter.java
   feeds100P26/src/test/
   feeds100P26/src/test/org/
   feeds100P26/src/test/org/jboss/
   feeds100P26/src/test/org/jboss/blog/
   feeds100P26/src/test/org/jboss/blog/session/
   feeds100P26/src/test/org/jboss/blog/session/feed/
   feeds100P26/src/test/org/jboss/blog/session/feed/posts/
   feeds100P26/src/test/org/jboss/blog/session/feed/posts/AggregatedFeedPostsTest.java
   feeds100P26/src/test/org/jboss/blog/session/merge/
   feeds100P26/src/test/org/jboss/blog/session/merge/test/
   feeds100P26/src/test/org/jboss/blog/session/merge/test/GenericsExample1.java
   feeds100P26/src/test/org/jboss/blog/session/merge/test/MergeServiceTest.java
   feeds100P26/src/test/org/jboss/blog/session/merge/test/MockEvents.java
   feeds100P26/src/test/org/jboss/blog/session/merge/test/MockTitleAsIdService.java
   feeds100P26/src/test/org/jboss/blog/session/parser/
   feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample.java
   feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample2.java
   feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample3.java
   feeds100P26/src/test/org/jboss/blog/tools/
   feeds100P26/src/test/org/jboss/blog/tools/FixHtmlExample1.java
   feeds100P26/src/test/org/jboss/blog/tools/FixHtmlTest.java
   feeds100P26/src/test/org/jboss/blog/tools/StripHtmlTest.java
   feeds100P26/src/test/org/jboss/blog/tools/TestTools.java
   feeds100P26/src/test/org/jboss/blog/tools/TitleToLinkTest.java
   feeds100P26/src/tools/
   feeds100P26/src/tools/org/
   feeds100P26/src/tools/org/jboss/
   feeds100P26/src/tools/org/jboss/blog/
   feeds100P26/src/tools/org/jboss/blog/tools/
   feeds100P26/src/tools/org/jboss/blog/tools/GeneralTools.java
   feeds100P26/src/tools/org/jboss/blog/tools/KeyNotMappedException.java
   feeds100P26/src/tools/org/jboss/blog/tools/KeySafeMap.java
   feeds100P26/src/tools/org/jboss/blog/tools/Pair.java
   feeds100P26/src/tools/org/jboss/blog/tools/StringTools.java
   feeds100P26/src/tools/org/jboss/blog/tools/search/
   feeds100P26/src/tools/org/jboss/blog/tools/search/UnrestrictedFeedFilter.java
   feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/
   feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/FeedBridge.java
   feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/StripHtmlBridge.java
   feeds100P26/src/tools/org/jboss/blog/tools/validator/
   feeds100P26/src/tools/org/jboss/blog/tools/validator/Regexp.java
   feeds100P26/src/tools/org/jboss/blog/tools/validator/RegexpValidator.java
   feeds100P26/view-portlet/
   feeds100P26/view-portlet/feed_not_found.jsp
   feeds100P26/view-portlet/view.jsp
   feeds100P26/view-portlet/view_main.jsp
   feeds100P26/view/
   feeds100P26/view/common/
   feeds100P26/view/common/ajax_status.xhtml
   feeds100P26/view/common/next_previous_navigation.xhtml
   feeds100P26/view/common/post.xhtml
   feeds100P26/view/emails/
   feeds100P26/view/emails/new_proposition_email.xhtml
   feeds100P26/view/emails/proposition_accepted.xhtml
   feeds100P26/view/emails/proposition_rejected.xhtml
   feeds100P26/view/emails/test_email.xhtml
   feeds100P26/view/error/
   feeds100P26/view/error/error.xhtml
   feeds100P26/view/error/feed_error.xhtml
   feeds100P26/view/error/post_error.xhtml
   feeds100P26/view/home.xhtml
   feeds100P26/view/images/
   feeds100P26/view/images/hdr_feed_gradient.gif
   feeds100P26/view/images/ico_linkarrow_blue.gif
   feeds100P26/view/images/portlethdr_home.gif
   feeds100P26/view/images/propose_blog.png
   feeds100P26/view/images/propose_blog_full.png
   feeds100P26/view/images/wait.gif
   feeds100P26/view/index.html
   feeds100P26/view/layout/
   feeds100P26/view/layout/menu.xhtml
   feeds100P26/view/layout/template.xhtml
   feeds100P26/view/manage/
   feeds100P26/view/manage/aggregated/
   feeds100P26/view/manage/aggregated/aggregated_add.xhtml
   feeds100P26/view/manage/aggregated/aggregated_edit.xhtml
   feeds100P26/view/manage/aggregated/aggregated_mod.xhtml
   feeds100P26/view/manage/aggregated/filter_add.xhtml
   feeds100P26/view/manage/configuration_manager.xhtml
   feeds100P26/view/manage/feed_add.xhtml
   feeds100P26/view/manage/feed_edit.xhtml
   feeds100P26/view/manage/feed_mod.xhtml
   feeds100P26/view/manage/feed_propose.xhtml
   feeds100P26/view/manage/group/
   feeds100P26/view/manage/group/group_add.xhtml
   feeds100P26/view/manage/group/group_edit.xhtml
   feeds100P26/view/manage/group/group_list.xhtml
   feeds100P26/view/manage/group/group_mod.xhtml
   feeds100P26/view/manage/highlights/
   feeds100P26/view/manage/highlights/highlights_edit.xhtml
   feeds100P26/view/manage/highlights/highlights_mod.xhtml
   feeds100P26/view/manage/highlights/post_add.xhtml
   feeds100P26/view/manage/index.xhtml
   feeds100P26/view/manage/individual/
   feeds100P26/view/manage/individual/individual_add.xhtml
   feeds100P26/view/manage/individual/individual_edit.xhtml
   feeds100P26/view/manage/individual/post_add.xhtml
   feeds100P26/view/manage/proposition/
   feeds100P26/view/manage/proposition/proposition_accept_1.xhtml
   feeds100P26/view/manage/proposition/proposition_accept_2.xhtml
   feeds100P26/view/manage/proposition/proposition_list.xhtml
   feeds100P26/view/manage/remote/
   feeds100P26/view/manage/remote/remote_add.xhtml
   feeds100P26/view/manage/remote/remote_edit.xhtml
   feeds100P26/view/manage/remote/remote_mod.xhtml
   feeds100P26/view/manage/remote/remote_propose.xhtml
   feeds100P26/view/manage/shotoku/
   feeds100P26/view/manage/shotoku/shotoku_add.page.xml
   feeds100P26/view/manage/shotoku/shotoku_add.xhtml
   feeds100P26/view/manage/shotoku/shotoku_edit.page.xml
   feeds100P26/view/manage/shotoku/shotoku_edit.xhtml
   feeds100P26/view/manage/shotoku/shotoku_mod.xhtml
   feeds100P26/view/manage/template/
   feeds100P26/view/manage/template/template_add.xhtml
   feeds100P26/view/manage/template/template_edit.xhtml
   feeds100P26/view/manage/template/template_list.xhtml
   feeds100P26/view/manage/template/template_mod.xhtml
   feeds100P26/view/manage/update_manager.xhtml
   feeds100P26/view/search/
   feeds100P26/view/search/search.xhtml
   feeds100P26/view/security/
   feeds100P26/view/security/account.xhtml
   feeds100P26/view/security/login.xhtml
   feeds100P26/view/security/security_group_add.xhtml
   feeds100P26/view/security/security_manager.xhtml
   feeds100P26/view/security/security_user_add.xhtml
   feeds100P26/view/stylesheet/
   feeds100P26/view/stylesheet/blog.css
   feeds100P26/view/stylesheet/org_layout.css
   feeds100P26/view/stylesheet/org_main.css
   feeds100P26/view/view/
   feeds100P26/view/view/feed.xhtml
   feeds100P26/view/view/feed_toolbar.xhtml
   feeds100P26/view/view/post.xhtml
   feeds100P26/view/view/right_box.xhtml
Modified:
   feeds100P26/
Log:



Property changes on: feeds100P26
___________________________________________________________________
Name: svn:ignore
   + dist
exploded-archives
.project


Added: feeds100P26/blog.iml
===================================================================
--- feeds100P26/blog.iml	                        (rev 0)
+++ feeds100P26/blog.iml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,549 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module relativePaths="true" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="jpa" name="JPA">
+      <configuration>
+        <setting name="validation-enabled" value="true" />
+        <setting name="provider-name" value="Hibernate" />
+        <datasource-mapping>
+          <factory-entry name="blog" value="Datasource" />
+        </datasource-mapping>
+        <deploymentDescriptor name="persistence.xml" url="file://$MODULE_DIR$/resources/META-INF/persistence-dev.xml" optional="false" version="1.0" />
+      </configuration>
+    </facet>
+    <facet type="hibernate" name="Hibernate">
+      <configuration>
+        <setting name="validation-enabled" value="true" />
+        <datasource-map />
+      </configuration>
+    </facet>
+    <facet type="javaeeApplication" name="javaEEApplication">
+      <configuration>
+        <descriptors>
+          <deploymentDescriptor name="application.xml" url="file://$MODULE_DIR$/resources/META-INF/application.xml" optional="false" version="5" />
+        </descriptors>
+        <building>
+          <setting name="EXPLODED_URL" value="file://" />
+          <setting name="EXPLODED_ENABLED" value="false" />
+          <setting name="JAR_URL" value="file://" />
+          <setting name="JAR_ENABLED" value="false" />
+          <setting name="BUILD_MODULE_ON_FRAME_DEACTIVATION" value="false" />
+          <setting name="BUILD_EXTERNAL_DEPENDENCIES" value="false" />
+          <setting name="EXCLUDE_EXPLODED_DIRECTORY" value="true" />
+        </building>
+        <packaging>
+          <containerElement type="facet" facetId="blog/ejb/EJB">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+          </containerElement>
+          <containerElement type="facet" facetId="blog/web/Web">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+            <attribute name="contextRoot" value="blogWeb" />
+          </containerElement>
+          <containerElement type="facet" facetId="seam-sources/javaeeApplication/javaEEApplication">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+          </containerElement>
+        </packaging>
+      </configuration>
+    </facet>
+    <facet type="ejb" name="EJB">
+      <configuration>
+        <descriptors>
+          <deploymentDescriptor name="ejb-jar.xml" url="file://$MODULE_DIR$/resources/META-INF/ejb-jar.xml" optional="false" version="3.0" />
+        </descriptors>
+        <ejbRoots>
+          <root url="file://$MODULE_DIR$/src/action" />
+          <root url="file://$MODULE_DIR$/src/model" />
+          <root url="file://$MODULE_DIR$/src/portal" />
+          <root url="file://$MODULE_DIR$/src/services" />
+          <root url="file://$MODULE_DIR$/src/shotoku" />
+          <root url="file://$MODULE_DIR$/src/test" />
+          <root url="file://$MODULE_DIR$/src/tools" />
+        </ejbRoots>
+        <building>
+          <setting name="EXPLODED_URL" value="file://" />
+          <setting name="EXPLODED_ENABLED" value="false" />
+          <setting name="JAR_URL" value="file://" />
+          <setting name="JAR_ENABLED" value="false" />
+          <setting name="BUILD_MODULE_ON_FRAME_DEACTIVATION" value="false" />
+          <setting name="BUILD_EXTERNAL_DEPENDENCIES" value="false" />
+          <setting name="EXCLUDE_EXPLODED_DIRECTORY" value="true" />
+          <setting name="RUN_EJB_VALIDATION" value="true" />
+        </building>
+        <packaging>
+          <containerElement type="library" name="Seam" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+          </containerElement>
+          <containerElement type="library" name="api-other" level="application">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+          </containerElement>
+          <containerElement type="library" name="hibernate" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+          </containerElement>
+          <containerElement type="library" name="javaee" level="application">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+          </containerElement>
+          <containerElement type="library" name="rome" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+          </containerElement>
+          <containerElement type="library" name="search" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+          </containerElement>
+          <containerElement type="library" name="testng" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+          </containerElement>
+          <containerElement type="library" name="velocity" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/drools-core.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/htmlcleaner1_6.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+            <url>jar://$APPLICATION_HOME_DIR$/lib/javaee.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/javassist.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/jericho-html-2.5.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/jsf-facelets.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/../jboss/lib/mysql.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/portal-common-lib.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/portal-identity-lib.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/portal-portlet-jsr168api-lib.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/shotoku-base.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/" />
+            <url>jar://$MODULE_DIR$/lib/urlrewritefilter.jar!/</url>
+          </containerElement>
+        </packaging>
+      </configuration>
+    </facet>
+    <facet type="web" name="Web">
+      <configuration>
+        <descriptors>
+          <deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/resources-portlet/WEB-INF/web.xml" optional="false" version="2.5" />
+        </descriptors>
+        <webroots>
+          <root url="file://$MODULE_DIR$/resources-portlet" relative="/" />
+        </webroots>
+        <building>
+          <setting name="EXPLODED_URL" value="file://" />
+          <setting name="EXPLODED_ENABLED" value="false" />
+          <setting name="JAR_URL" value="file://" />
+          <setting name="JAR_ENABLED" value="false" />
+          <setting name="BUILD_MODULE_ON_FRAME_DEACTIVATION" value="false" />
+          <setting name="BUILD_EXTERNAL_DEPENDENCIES" value="false" />
+          <setting name="EXCLUDE_EXPLODED_DIRECTORY" value="true" />
+          <setting name="RUN_JASPER_VALIDATION" value="true" />
+          <setting name="BUILD_ONLY_WEB_RESOURCES" value="false" />
+        </building>
+        <packaging>
+          <containerElement type="module" name="blog">
+            <attribute name="method" value="1" />
+            <attribute name="URI" value="/WEB-INF/classes" />
+          </containerElement>
+          <containerElement type="library" name="Seam" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+          </containerElement>
+          <containerElement type="library" name="api-other" level="application">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+          </containerElement>
+          <containerElement type="library" name="hibernate" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+          </containerElement>
+          <containerElement type="library" name="javaee" level="application">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+          </containerElement>
+          <containerElement type="library" name="rome" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+          </containerElement>
+          <containerElement type="library" name="search" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+          </containerElement>
+          <containerElement type="library" name="testng" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+          </containerElement>
+          <containerElement type="library" name="velocity" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/drools-core.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/htmlcleaner1_6.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="&lt;N/A&gt;" />
+            <url>jar://$APPLICATION_HOME_DIR$/lib/javaee.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/javassist.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/jericho-html-2.5.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/jsf-facelets.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/../jboss/lib/mysql.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/portal-common-lib.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/portal-identity-lib.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/portal-portlet-jsr168api-lib.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/shotoku-base.jar!/</url>
+          </containerElement>
+          <containerElement type="library" level="module">
+            <attribute name="method" value="0" />
+            <attribute name="URI" value="/WEB-INF/lib" />
+            <url>jar://$MODULE_DIR$/lib/urlrewritefilter.jar!/</url>
+          </containerElement>
+        </packaging>
+      </configuration>
+      <facet type="jsf" name="JSF" implicit="true">
+        <configuration />
+      </facet>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/action" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/model" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/portal" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/services" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/shotoku" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/tools" isTestSource="false" />
+      <excludeFolder url="file://$MODULE_DIR$/dist" />
+      <excludeFolder url="file://$MODULE_DIR$/exploded-archives" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="api-other" level="application" />
+    <orderEntry type="library" name="javaee" level="application" />
+    <orderEntry type="module-library">
+      <library name="Seam">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam-debug.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam-ui.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam-remoting.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam-pdf.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam-ioc.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam-mail.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jboss-seam-gen.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="hibernate">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/hibernate-entitymanager.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/hibernate-commons-annotations.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/hibernate-annotations.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/hibernate-search.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/hibernate.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/hibernate-validator.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/commons-logging.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/commons-collections.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/commons-lang.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/commons-beanutils.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/cglib.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/commons-digester.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/asm.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/antlr.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/dom4j.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="rome">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/rome-0.9.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/jdom.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/jsf-facelets.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="testng">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/testng.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/easymock.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="velocity">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/velocity-1.5.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/velocity-dep-1.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/urlrewritefilter.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library name="search">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/lucene-core.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/hibernate-commons-annotations.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/hibernate-search.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="file://$MODULE_DIR$/../jboss/hibernate/search/src/java" />
+        </SOURCES>
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/jericho-html-2.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/javassist.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/htmlcleaner1_6.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../jboss/lib/mysql.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/drools-core.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/portal-portlet-jsr168api-lib.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/shotoku-base.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/portal-identity-lib.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/portal-common-lib.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$APPLICATION_HOME_DIR$/lib/javaee.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../.maven/repository/jboss/jars/jgroups.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../.maven/repository/jboss/jars/jboss-cache.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../.maven/repository/jboss/jars/jboss-system.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/../.maven/repository/jboss/jars/jboss-jmx.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntry type="module-library">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/let-tag.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
+    <orderEntryProperties />
+  </component>
+</module>
+

Added: feeds100P26/build-design.properties
===================================================================
--- feeds100P26/build-design.properties	                        (rev 0)
+++ feeds100P26/build-design.properties	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1 @@
+debug=true

Added: feeds100P26/build-dev.properties
===================================================================
--- feeds100P26/build-dev.properties	                        (rev 0)
+++ feeds100P26/build-dev.properties	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,2 @@
+debug=true
+action.dir=WEB-INF/dev

Added: feeds100P26/build-prod.properties
===================================================================
--- feeds100P26/build-prod.properties	                        (rev 0)
+++ feeds100P26/build-prod.properties	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,4 @@
+debug=false
+
+
+

Added: feeds100P26/build.properties
===================================================================
--- feeds100P26/build.properties	                        (rev 0)
+++ feeds100P26/build.properties	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,6 @@
+#jboss.home = /Users/adamwarski/portal-extensions/feeds/binaries
+jboss.home = /Users/adamwarski/jboss/jboss-4.2
+#jboss.home = /Users/adamwarski/jboss/jboss-4.0.5
+profile = dev
+#profile = prod
+#jboss.home = /Users/adamwarski/jboss/jboss-design
\ No newline at end of file

Added: feeds100P26/build.xml
===================================================================
--- feeds100P26/build.xml	                        (rev 0)
+++ feeds100P26/build.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,477 @@
+<?xml version="1.0"?>
+
+<project name="blog" default="deploy" basedir=".">
+    <!-- Give user a chance to override without editing this file or typing -D -->
+    <property file="${basedir}/build.properties" />
+
+    <property name="profile" value="dev" />
+    <property file="${basedir}/build-${profile}.properties" />
+
+    <!-- set global properties for this build -->
+    <property name="project.name" value="blog"/>
+    <property name="dist.dir" value="dist" />
+    <property name="src.model.dir" value="src/model" />
+    <property name="src.action.dir" value="src/action" />
+    <property name="src.services.dir" value="src/services" />
+    <property name="src.tools.dir" value="src/tools" />
+    <property name="src.test.dir" value="src/test" />
+    <property name="src.portal.dir" value="src/portal" />
+    <property name="src.shotoku.dir" value="src/shotoku" />
+    <property name="lib.dir" value="lib" />
+
+    <property name="jar.impl.name" value="${project.name}-impl.jar" />
+    <property name="jar.api.name" value="${project.name}.jar" />
+    <property name="jar.shotoku.name" value="${project.name}-shotoku.jar" />
+    <property name="jar.portal.name" value="${project.name}-portal.jar" />
+    <property name="portal.name" value="${project.name}-portal.war" />
+
+    <property name="ear.dir" value="exploded-archives/${project.name}.ear" />
+    <property name="jar.impl.dir" value="exploded-archives/${jar.impl.name}" />
+    <property name="jar.api.dir" value="exploded-archives/${jar.api.name}" />
+    <property name="jar.shotoku.dir" value="exploded-archives/${jar.shotoku.name}" />
+    <property name="jar.portal.dir" value="exploded-archives/${jar.portal.name}" />
+    <property name="war.dir" value="exploded-archives/${project.name}.war" />
+    <property name="portal.dir" value="exploded-archives/${portal.name}" />
+    <property name="test.dir" value="test-build" />
+    <property name="bootstrap.dir" value="${basedir}/bootstrap" />
+    <property name="deploy.dir" value="${jboss.home}/server/default/deploy" />
+    <property name="deploy.lib.dir" value="${jboss.home}/server/default/lib" />
+    <property name="ear.deploy.dir" value="${deploy.dir}/${project.name}.ear" />
+    <property name="jar.deploy.dir" value="${ear.deploy.dir}/${jar.impl.name}" />
+    <property name="jar.shotoku.deploy.dir" value="${ear.deploy.dir}/${jar.shotoku.name}" />
+    <property name="jar.portal.deploy.dir" value="${ear.deploy.dir}/${jar.portal.name}" />
+    <property name="war.deploy.dir" value="${ear.deploy.dir}/${project.name}.war" />
+    <property name="portal.deploy.dir" value="${ear.deploy.dir}/${portal.name}" />
+    <property name="testng.jar" value="${basedir}/lib/testng.jar" />
+    <property name="javac.debug" value="true" />
+    <property name="javac.deprecation" value="false" />
+    <property name="debug" value="false" />
+
+    <!--Properties for validating configuration files -->
+    <property name="validate.resources.dir" value="${basedir}/exploded-archives" />
+    <property name="schema.dir" value="${basedir}/exploded-archives/schemas" />
+    <property name="src.schema.dir" value="${schema.dir}/org/jboss/seam"/>
+    <property name="schema.version" value="2.0" />
+
+    <fileset id="lib" dir="${lib.dir}">
+        <include name="*.jar" />
+    </fileset>
+
+    <path id="build.api.classpath">
+        <fileset refid="lib" />
+    </path>
+
+    <path id="build.impl.classpath">
+        <fileset refid="lib" />
+        <pathelement path="${jar.api.dir}" />
+    </path>
+
+    <path id="build.dep.classpath">
+        <fileset refid="lib" />
+        <pathelement path="${jar.api.dir}" />
+        <pathelement path="${jar.impl.dir}" />
+    </path>
+
+    <target name="init" description="Initialize the build">
+        <mkdir dir="${jar.impl.dir}" />
+        <mkdir dir="${jar.api.dir}" />
+        <mkdir dir="${jar.shotoku.dir}" />
+        <mkdir dir="${jar.portal.dir}" />
+        <mkdir dir="${ear.dir}" />
+        <mkdir dir="${war.dir}" />
+        <mkdir dir="${portal.dir}" />
+        <mkdir dir="${portal.dir}/WEB-INF" />
+        <mkdir dir="${portal.dir}/WEB-INF/lib" />
+        <mkdir dir="${dist.dir}" />
+    </target>
+
+    <target name="compile" depends="init"
+            description="Compile the Java source code">
+        <javac classpathref="build.api.classpath"
+               destdir="${jar.api.dir}"
+               debug="${javac.debug}"
+               deprecation="${javac.deprecation}"
+               nowarn="on">
+            <src path="${src.tools.dir}" />
+            <src path="${src.services.dir}" />
+            <src path="${src.model.dir}" />
+        </javac>
+        <javac classpathref="build.impl.classpath"
+               destdir="${jar.impl.dir}"
+               debug="${javac.debug}"
+               deprecation="${javac.deprecation}"
+               nowarn="on">
+            <src path="${src.action.dir}" />
+        </javac>
+        <javac classpathref="build.dep.classpath"
+               destdir="${jar.portal.dir}"
+               debug="${javac.debug}"
+               deprecation="${javac.deprecation}"
+               nowarn="on">
+            <src path="${src.portal.dir}" />
+        </javac>
+        <javac classpathref="build.dep.classpath"
+               destdir="${jar.shotoku.dir}"
+               debug="${javac.debug}"
+               deprecation="${javac.deprecation}"
+               nowarn="on">
+            <src path="${src.shotoku.dir}" />
+        </javac>
+    </target>
+
+    <target name="jar" depends="compile"
+            description="Build the distribution .jar file">
+        <!-- Jars for model and services -->
+        <jar jarfile="${dist.dir}/${jar.api.name}" basedir="${jar.api.dir}"/>
+
+        <!-- Jar for action -->
+        <copy todir="${jar.impl.dir}">
+            <fileset dir="${basedir}/resources">
+                <include name="seam.properties" />
+            </fileset>
+        </copy>
+
+        <!-- TODO: remove this marker for scanner -->
+        <copy todir="${jar.shotoku.dir}">
+            <fileset dir="${basedir}/resources">
+                <include name="seam.properties" />
+            </fileset>
+        </copy>
+
+        <!-- TODO: remove this marker for scanner -->
+        <copy todir="${jar.portal.dir}">
+            <fileset dir="${basedir}/resources">
+                <include name="seam.properties" />
+            </fileset>
+        </copy>
+
+        <copy todir="${jar.impl.dir}/META-INF">
+            <fileset dir="${basedir}/resources/META-INF">
+                <include name="ejb-jar.xml" />
+            </fileset>
+        </copy>
+        <copy tofile="${jar.impl.dir}/META-INF/persistence.xml"
+              file="${basedir}/resources/META-INF/persistence-${profile}.xml"
+              overwrite="true"/>
+
+        <!-- Shotoku -->
+        <jar jarfile="${dist.dir}/${jar.shotoku.name}" basedir="${jar.shotoku.dir}"/>
+    </target>
+
+    <target name="war" depends="compile"
+            description="Build the distribution .war file">
+        <copy todir="${war.dir}">
+            <fileset dir="${basedir}/view" />
+        </copy>
+        <copy todir="${war.dir}">
+            <fileset dir="${basedir}/resources">
+                <include name="templates/**/*.*" />
+            </fileset>
+        </copy>
+        <copy todir="${war.dir}/WEB-INF">
+            <fileset dir="${basedir}/resources/WEB-INF">
+                <include name="*.*"/>
+                <include name="classes/**/*.*"/>
+                <exclude name="classes/**/*.class"/>
+                <exclude name="web-*.xml" />
+            </fileset>
+            <filterset>
+                <filter token="debug" value="${debug}" />
+                <filter token="jndiPattern" value="${project.name}/#{ejbName}/local" />
+                <filter token="embeddedEjb" value="false" />
+            </filterset>
+        </copy>
+        <copy tofile="${war.dir}/WEB-INF/web.xml" file="${basedir}/resources/WEB-INF/web-${profile}.xml">
+            <filterset>
+                <filter token="debug" value="${debug}" />
+                <filter token="jndiPattern" value="${project.name}/#{ejbName}/local" />
+                <filter token="embeddedEjb" value="false" />
+            </filterset>
+        </copy>
+        <copy todir="${war.dir}/WEB-INF">
+            <fileset dir="${basedir}/resources/WEB-INF">
+                <include name="lib/*.*"/>
+                <include name="classes/**/*.class"/>
+            </fileset>
+        </copy>
+        <!--<copy todir="${war.dir}/WEB-INF/lib">
+              <fileset dir="${lib.dir}">
+                  moved to ear goal
+              </fileset>
+      </copy>  -->
+        <copy todir="${war.dir}/WEB-INF/classes">
+            <fileset dir="${basedir}/resources">
+                <include name="messages*.properties"/>
+            </fileset>
+        </copy>
+
+        <copy todir="${portal.dir}">
+            <fileset dir="${basedir}/resources-portlet">
+                <include name="**/*"/>
+            </fileset>
+        </copy>
+        <copy todir="${portal.dir}">
+            <fileset dir="${basedir}/view-portlet">
+                <include name="**/*"/>
+            </fileset>
+        </copy>
+        <copy todir="${portal.dir}/WEB-INF/lib">
+            <fileset dir="${basedir}/lib">
+                <include name="jstl.jar" />
+                <include name="standard.jar" />
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="ear" description="Build the EAR">
+        <copy todir="${ear.dir}">
+            <fileset dir="${basedir}/resources">
+                <include name="*jpdl.xml" />
+                <include name="hibernate.cfg.xml" />
+                <include name="jbpm.cfg.xml" />
+                <include name="*.drl" />                
+                <include name="velocity.properties"/>
+                <include name="treecache.xml" />
+                <include name="blog-ehcache.xml" />
+            </fileset>
+            <fileset dir="${lib.dir}">
+                <include name="jboss-seam.jar" />  
+            </fileset>
+            <fileset dir="${basedir}">
+                <include name="lib/jbpm*.jar" />
+                <include name="lib/jboss-el.jar" />
+                <include name="lib/drools-*.jar"/>
+                <include name="lib/janino-*.jar"/>
+                <include name="lib/antlr-*.jar"/>
+                <include name="lib/mvel*.jar"/>
+                <include name="lib/richfaces-api*.jar" />
+                <include name="lib/rome*.jar" />
+
+                <include name="lib/jgroups*.jar" />
+                <include name="lib/jboss-cache*.jar" />
+
+                <!--<include name="lib/ehcache*.jar" />-->
+
+                <!-- Moved to server/default/lib
+                <include name="lib/hibernate*.jar" />
+                
+                <include name="lib/lucene-core.jar" />
+                <include name="lib/hibernate-search.jar" />
+                <include name="lib/hibernate-commons-annotations.jar" />-->
+
+                <!-- moved from the war goal -->
+                <include name="lib/richfaces-impl*.jar" />
+                <include name="lib/richfaces-ui*.jar" />
+                <include name="lib/oscache*.jar" />
+                <include name="lib/commons-digester.jar" />
+                <include name="lib/commons-beanutils.jar" />
+                <include name="lib/jsf-facelets.jar" />
+                <include name="lib/velocity*.jar" />
+                <include name="lib/urlrewritefilter.jar" />
+                <include name="lib/jboss-seam-*.jar" />
+                <exclude name="lib/jboss-seam-gen.jar" />
+
+                <include name="lib/let-tag.jar" />
+            	
+        	<!-- Luca Stancapiano - added to use jbossblog as portlet -->
+                <include name="lib/shotoku-base.jar" />
+                <include name="lib/commons-configuration-1.6.jar" />   <!-- not included -->
+                <include name="lib/commons-lang-2.4.jar" />   <!-- not included --> 
+            <!--<include name="lib/ehcache*.jar" />
+                <include name="lib/jdom*.jar" />
+                <include name="lib/jericho-html-*.jar" />
+                <include name="lib/htmlcleaner*.jar" /> -->
+                        
+                <!--<include name="lib/hibernate3.jar" />             
+                <include name="lib/hibernate-annotations.jar" />         
+                <include name="lib/hibernate-entitymanager.jar" /> 
+                <include name="lib/hibernate-commons-annotations.jar" />  
+                <include name="lib/hibernate.jar" />                    
+                <include name="lib/hibernate-search.jar" />            
+                <include name="lib/hibernate-validator.jar" /> -->
+
+                <!-- <include name="lib/lucene*.jar" /> -->
+                <!-- end -->
+            </fileset>
+        </copy>
+        <copy todir="${ear.dir}/META-INF">
+            <fileset dir="${basedir}/resources/META-INF">
+                <include name="application.xml" />
+                <include name="jboss-app.xml" />
+                <include name="jbossblog.taglib.xml" />
+                <include name="security.drl" />
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="archive" depends="jar,war,ear"
+            description="Package the archives">
+        <jar jarfile="${dist.dir}/${jar.impl.name}" basedir="${jar.impl.dir}"/>
+        <jar jarfile="${dist.dir}/${jar.shotoku.name}" basedir="${jar.shotoku.dir}"/>
+        <jar jarfile="${dist.dir}/${jar.portal.name}" basedir="${jar.portal.dir}"/>
+        <jar jarfile="${dist.dir}/${project.name}.war" basedir="${war.dir}"/>
+        <jar jarfile="${dist.dir}/${portal.name}" basedir="${portal.dir}"/>
+        <jar jarfile="${dist.dir}/${project.name}.ear">
+            <fileset dir="${ear.dir}"/>
+            <fileset dir="${dist.dir}">
+                <include name="${jar.impl.name}"/>
+                <include name="${jar.shotoku.name}"/>
+                <include name="${jar.portal.name}"/>
+                <include name="${project.name}.war"/>
+                <include name="${portal.name}"/>
+            </fileset>
+        </jar>
+    </target>
+
+    <target name="datasource">
+        <fail unless="jboss.home">jboss.home not set</fail>
+        <copy todir="${deploy.dir}">
+            <fileset dir="${basedir}/resources">
+                <include name="${project.name}-${profile}-ds.xml" />
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="prepare-as-lib">
+        <copy todir="${deploy.lib.dir}" overwrite="true">
+            <fileset dir="${basedir}/lib">
+                <include name="ehcache*.jar" />
+                <include name="jdom*.jar" />
+                <include name="jericho-html-*.jar" />
+                <include name="htmlcleaner*.jar" />
+                <include name="hibernate*.jar" />
+                <include name="lucene*.jar" />
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="explode" depends="jar,war,ear"
+            description="Deploy the exploded archive">
+        <fail unless="jboss.home">jboss.home not set</fail>
+
+        <mkdir dir="${jar.deploy.dir}"/>
+        <mkdir dir="${war.deploy.dir}"/>
+        <mkdir dir="${portal.deploy.dir}"/>
+
+        <copy todir="${jar.deploy.dir}">
+            <fileset dir="${jar.impl.dir}"/>
+        </copy>
+        <copy todir="${jar.shotoku.deploy.dir}">
+            <fileset dir="${jar.shotoku.dir}"/>
+        </copy>
+        <copy todir="${jar.portal.deploy.dir}">
+            <fileset dir="${jar.portal.dir}"/>
+        </copy>
+        <copy todir="${war.deploy.dir}">
+            <fileset dir="${war.dir}"/>
+        </copy>
+        <copy todir="${portal.deploy.dir}">
+            <fileset dir="${portal.dir}"/>
+        </copy>
+        <copy todir="${ear.deploy.dir}">
+            <fileset dir="${ear.dir}"/>
+        </copy>
+        
+        <copy todir="${deploy.dir}">
+            <fileset dir="${dist.dir}">
+                <include name="${jar.api.name}" />
+            </fileset>
+        </copy>
+
+        <!--<copy todir="${deploy.dir}" file="${basedir}/resources/blog-ehcache.xml" />-->
+
+        <ant target="prepare-as-lib" />
+    </target>
+
+    <target name="unexplode" description="Undeploy the exploded archive">
+        <delete failonerror="no">
+            <fileset dir="${ear.deploy.dir}">
+                <exclude name="**/*.jar"/>
+            </fileset>
+        </delete>
+        <delete dir="${ear.deploy.dir}" failonerror="no"/>
+
+        <delete file="${deploy.dir}/${jar.api.name}" />
+    </target>
+
+    <target name="restart" depends="clean,unexplode,explode" description="Restart the exploded archive">
+        <touch file="${ear.deploy.dir}/META-INF/application.xml"/>
+    </target>
+
+    <target name="deploy" depends="clean,archive" description="Deploy to JBoss AS">
+        <fail unless="jboss.home">jboss.home not set</fail>
+        <copy todir="${deploy.dir}" file="${dist.dir}/${project.name}.ear" />
+        <copy todir="${deploy.dir}" file="${dist.dir}/${jar.api.name}" />
+        <ant target="prepare-as-lib" />
+    </target>
+
+    <target name="undeploy" description="Undeploy the example from JBoss">
+        <delete file="${deploy.dir}/${project.name}.ear" />
+        <delete file="${deploy.dir}/${jar.api.name}" />
+    </target>
+
+    <target name="clean" description="Cleans up the build directory">
+        <delete dir="${dist.dir}"/>
+        <delete dir="${ear.dir}"/>
+        <delete dir="${war.dir}"/>
+        <delete dir="${portal.dir}"/>
+        <delete dir="${jar.impl.dir}"/>
+        <delete dir="${jar.api.dir}"/>
+        <delete dir="${jar.portal.dir}"/>
+        <delete dir="${src.schema.dir}" failonerror="no"/>
+        <delete dir="${basedir}/test-report"/>
+        <delete dir="${basedir}/test-output"/>
+    </target>
+
+    <target name="clean-all" depends="undeploy,unexplode,clean" />
+
+    <target name="compiletest" unless="eclipse.running" description="Compile the Java source code for the tests">
+        <mkdir dir="${test.dir}"/>
+        <javac classpathref="build.impl.classpath"
+               destdir="${test.dir}"
+               debug="${javac.debug}"
+               deprecation="${javac.deprecation}"
+               nowarn="on">
+            <src path="${src.action.dir}" />
+            <src path="${src.model.dir}" />
+            <src path="${src.test.dir}" />
+        </javac>
+    </target>
+
+    <target name="buildtest" depends="compiletest" description="Build the tests">
+        <copy todir="${test.dir}">
+            <fileset dir="${basedir}/resources">
+                <exclude name="META-INF/persistence*.xml"/>
+                <exclude name="import*.sql"/>
+                <exclude name="${project.name}-*-ds.xml"/>
+            </fileset>
+        </copy>
+        <copy tofile="${test.dir}/META-INF/persistence.xml"
+              file="${basedir}/resources/META-INF/persistence-test.xml"
+              overwrite="true"/>
+        <copy tofile="${test.dir}/import.sql"
+              file="${basedir}/resources/import-test.sql"
+              overwrite="true"/>
+        <copy todir="${test.dir}" flatten="true">
+            <fileset dir="${src.test.dir}">
+                <include name="**/*Test.xml" />
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="test" depends="buildtest" description="Run the tests">
+        <taskdef resource="testngtasks" classpath="${testng.jar}" />
+        <path id="test.path">
+            <path path="${test.dir}" />
+            <fileset dir="${lib.dir}/test">
+                <include name="*.jar"/>
+            </fileset>
+            <path path="${bootstrap.dir}" />
+            <path refid="build.impl.classpath" />
+        </path>
+        <testng outputdir="${basedir}/test-report">
+            <classpath refid="test.path" />
+            <xmlfileset dir="${test.dir}" includes="*Test.xml" />
+        </testng>
+    </target>
+</project>

Added: feeds100P26/docs/admin_link.png
===================================================================
(Binary files differ)


Property changes on: feeds100P26/docs/admin_link.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/docs/group_header.png
===================================================================
(Binary files differ)


Property changes on: feeds100P26/docs/group_header.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/docs/tutorial.html
===================================================================
--- feeds100P26/docs/tutorial.html	                        (rev 0)
+++ feeds100P26/docs/tutorial.html	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,371 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE html
+        PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <title>JBoss.ORG Feeds tutorial</title>
+    <link rel="stylesheet" href="http://www.jboss.org/files/portletcontainer/docs/2.0.0.CR1/css/jbossorg.css" type="text/css"/>
+</head>
+<body>
+<p id="title">
+    <a href="http://www.jboss.org" class="jbossOrg_href">
+        <strong>
+            JBoss.org
+        </strong>
+    </a>
+    <a href="http://labs.jboss.com/projects/docs" class="commDoc_href">
+        <strong>
+            Community Documentation
+        </strong>
+    </a>
+</p>
+<div class="book" lang="en">
+    <div class="titlepage">
+        <div>
+            <div>
+
+                <h1 class="title">
+                    <a id="d0e1"/>JBoss.ORG Feeds</h1>
+            </div>
+            <div>
+                <h2 class="subtitle">Tutorial</h2>
+            </div>
+            <div>
+                <div class="author">
+
+                    <h3 class="author">
+                        <span class="firstname">Adam</span>
+                        <span class="surname">Warski</span>
+                    </h3>
+                    <code class="email">&lt;<a href="mailto:adam.warski at jboss.org">adam.warski at jboss.org</a>&gt;</code>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">1. About</h2>
+            </div>
+        </div>
+    </div>
+    <p>The Feeds application has two main functions:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li><span class="bold"><strong>aggregating</strong></span> blogs of JBoss employees</li>
+            <li><span class="bold"><strong>archiving</strong></span> posts</li>
+        </ul>
+    </div>
+    <p>It is not meant to be a blog-authoring application. So you should have an outside blog (on Blogger,
+    Wordpress or wherever), which you use for authoring, and then aggregate it into the feeds system.</p>
+
+    <p>If you have any questions about the application, please mail me. If you see a bug, mail me or create
+    a bug in <a href="http://jira.jboss.com/jira/browse/JBBLOG">JIRA</a>. The same applies for feature suggestions,
+    improvements, .... Thanks :).</p>
+
+    <p>Also, when using the application, you'll probably notice a "Tips" box on the right - it often contains
+    description of configuration parameters, explains the meaning of some options etc.</p>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">2. Groups, feeds, posts</h2>
+            </div>
+        </div>
+    </div>
+    <p>The application is organized around <span class="bold"><strong>feeds</strong></span>. A feed is linked to an
+    arbitrary number of <span class="bold"><strong>posts</strong></span>. However, the fact that a feed is linked to
+    a post doesn't imply that it is the owner of that post. A feed owns a post if that post originated from that feed;
+    for some types of feeds, there are no posts which originate from them, see below.</p>
+
+    <p>Feeds are organized in <span class="bold"><strong>groups</strong></span>. Basically, one project should have
+    one group, and all feeds that belong to that project should be in that group. For example, all feeds of blogs
+    of people involved in a project should belong to that project's feed group.</p>
+
+    <p>Each post, feed and group have a unique id, which is a sequence of lowercase letters, numbers and _. The
+    id for a post is determined basing on its title, and the ids for feeds and groups are determined by their
+    creators.</p>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">3. Types of feeds</h2>
+            </div>
+        </div>
+    </div>
+    <p>There are currently 5 types of feeds. The "type" of a feed is invisible to the user. It just determines the
+    way that posts of a feed are determined. The types are:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li><span class="bold"><strong>remote feed</strong></span> - a feed which reads and archives posts
+            from an XML feed, given as an internet address. The XML feed can be of any type (RSS2/ATOM/RDF).
+            All posts that are linked to this feed are also owned
+            by this feed, so if you delete a remote feed, you also delete all posts that where archived.</li>
+            <li><span class="bold"><strong>aggregated feed</strong></span> - a feed which combines posts from other
+            feeds, or groups. For example, you may want to create an aggregated feed for all blog feeds of your
+            project. For simplicity, you may simply aggregate posts from feeds that belong to your group,
+            by aggregating that group. You may include posts from any number of feeds/ groups. Moreover, you
+            can filter which posts from a feed/ group you want, by adding post filters. Such filters may accept only
+            posts by a specific author, or from a specific category. This type of feed doesn't own the posts that
+            are linked to it.</li>
+            <li><span class="bold"><strong>shotoku feed</strong></span> - a feed which reads posts
+            from a directory in SVN. The posts are files in that directory; post titles are the svn 'title' property
+            on that files. This feed also owns all posts that are linked to it.</li>
+            <li><span class="bold"><strong>highlights feed</strong></span> - a feed which lets you choose posts
+            that will appear in it, and their order. This can be very useful if you'd like to have on your webpage
+            a "top news" or "top blog posts of the month" window. Of course, this feed doesn't own the posts that
+            are linked to it.</li>
+            <li><span class="bold"><strong>individual posts feed</strong></span> - a feed which lets you include
+            posts from arbitrary remote feeds individually - that is, without including the whole feed in the
+            system. This may be useful if you want to promote some community posts, for example.</li>
+        </ul>
+    </div>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">4. Archiving</h2>
+            </div>
+        </div>
+    </div>
+    <p>All feeds are updated in regular intervals of time (by default, every 15 minutes). In case of a remote feed
+    it means that if there are new posts, they will get merged with the posts that are currently held in the database.
+    No posts are deleted during merging, so if a post disappears from a remote feed (because, for example, the
+    remote feed has only 10 newest posts), it won't disappear from the feeds system.</p>
+
+    <p>This has one drawback, though: if you are not entirely concious when you get home in the evening, and decide to
+    write a very sincere post, with a degree of sincerity which isn't normally viewed as good manners, and in the
+    morning realize that you should delete the post, you'll have to delete it from the feeds sytem manually.</p>
+
+    <p>If you change your post, for example alter the content, the change will be picked up and the post in the
+    database will also be updated. With one exception: when you change the published date. That will be seen as a new
+    post.</p>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">5. Viewing</h2>
+            </div>
+        </div>
+    </div>
+    <p>Users can view the feeds and posts using the webpage, or using an ATOM feed. When viewing a post (either
+    as part of a feed page, or as part of a post page), there is always a link back to the original post, so that
+    users can, for example, add comments. The URL schemes are:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li><code>http://labs.jboss.com/feeds/view/FEED_ID?from=X</code> - for viewing a feed with id
+            <code>FEED_ID</code>, starting from post number <code>X</code>.</li>
+            <li><code>http://labs.jboss.com/feeds/xml/FEED_ID?type=atom</code> - for getting an ATOM feed of a feed
+            with id <code>FEED_ID</code>.</li>
+            <li><code>http://labs.jboss.com/feeds/post/POST_ID</code> - for viewing a single post, with id
+            <code>POST_ID</code>.</li>
+        </ul>
+    </div>
+    <p>Because all posts are archived, the URLs for posts will be valid even after the posts disappear from a blog
+    feed (normally blog feeds contain only the 10 or so newest posts). Also, the page for viewing a single post contains
+    a link to the owning feed of that post.</p>
+
+    <p>How many posts appear on each page when viewing a feed, and how many posts appear in the ATOM feed, depends
+    on feed settings. See the section on administration for information on how to set these and other properties.</p>
+
+    <p>You can also customize the right part of the "View feed" page a little. For example, you may want to include
+    a "Back to MyProject page" link and a logo. You can do that, by setting a "Group header". It is a piece of HTML,
+    which will be displayed right below the "Feeds home" link, when viewing any feed or post belonging to that group.
+    To set it, got to manage home -> manage groups -> edit group.</p>
+
+    <p>Here is an example:</p>
+
+    <p><img src="group_header.png" alt="" /></p>
+
+    <p>And the corresponding HTML:</p>
+
+    <code>
+&lt;a href="/jbossjbpm"&gt;<br />
+&lt;img width="200" src="/file-access/default/members/jbossjbpm/images/jbpm_logo.png" alt="" /&gt;<br />
+&lt;/a&gt;<br />
+<br />
+&lt;ul&gt;<br />
+   &lt;li&gt;&lt;a href="/jbossjbpm"&gt;Back to the JBoss jBPM project page&lt;/a&gt;&lt;/li&gt;<br />
+&lt;/ul&gt;
+    </code>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">6. Community involvement</h2>
+            </div>
+        </div>
+    </div>
+    <p>Normal users (which aren't JBoss employees) have the possibility to
+    <span class="bold"><strong>propose</strong></span> their feeds for inclusion in the system. Of course, the
+    posts have to be JBoss- or JBoss project X- related. Users can only add remote feeds, and before they get
+    included in the system, they have to be accepted by a group administrator (when proposing a blog, users state to
+    which group they think it should belong). You may also create separate groups
+    for community and employee blogs.</p>
+
+    <p>To receive notifications about new proposed feeds in your group, you have to set the "Administrator e-mail"
+    field in group administration (see the "Administration" section, page: Manage feeds -> Manage feed groups ->
+    Group X edit).</p>
+
+    <p>To accept or reject proposed feeds, go to manage home -> pending feed propositions. There you'll see a table
+    presenting the proposed feeds, with the feed address (so that you can actually look at the posts) and a
+    regular expression determining posts from which categories will be included. When you click "Accept", you'll have
+    the possibility to change the properties of the feed, as well as see which posts will be saved.</p>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">7. Permissions</h2>
+            </div>
+        </div>
+    </div>
+    <p>You can have the following administrative permissions:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li><span class="bold"><strong>group X manager</strong></span> - having this role, you can edit
+            the name, id, header of the given group. You can also add new feeds, and edit properties of existing ones.
+            Moreover, you're able to create new feed groups. Finally, you can administrate security for that group -
+            that is, grant other users/user groups the permission to be a group or feed manager</li>
+            <li><span class="bold"><strong>feed X manager</strong></span> - having this role, you can edit
+            the properties of the given feed</li>
+            <li><span class="bold"><strong>view feed X</strong></span> - having this role, you view a given
+            restricted feed</li>
+        </ul>
+    </div>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">8. Administration</h2>
+            </div>
+        </div>
+    </div>
+    <p>To access the administration of feeds, simply login using your jboss.com login and password. If you are
+    authorized to manage anything, you should see additional links like this, near the top, on the right:</p>
+
+    <p><img src="admin_link.png" alt=""/></p>
+
+    <p>If you don't see it, then you don't have any administrative rights. Mail your manager or me if you think
+    you should have them. Basically, if you are a manager of a project in Labs (that is, after loggin in to
+    labs.jboss.com you can see the "Edit project" button), you should be a manager of a corresponding group
+    in feeds.</p>
+
+    <p>After clicking that link, you'll see the following options:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li><span class="bold"><strong>Manage feed groups</strong></span> - lets you edit and add feed groups
+            (for example, group headers)</li>
+            <li><span class="bold"><strong>Manage security</strong></span> - lets you configure permissions for
+            accessing feeds and groups that you have permissions to</li>
+            <li>for each feed: <span class="bold"><strong>Edit common</strong></span> - lets you edit "common"
+            properties of a feed, that is, properties that all feed types share, like the number of posts in
+            a feed, number of posts displayed on a "View feed" page, title and author of the feed, etc.</li>
+            <li>for each feed: <span class="bold"><strong>Edit specific</strong></span> - lets you edit "specifc"
+            properties of a feed, that is, properties unique to that type of feed. That is, for a remote feed -
+            the address of the XML, for an aggregated feed - what is aggregated and with what filters, for a
+            highlights feed - the order of posts, for a shotoku feed - the directory, from which posts are read.</li>
+        </ul>
+    </div>
+
+    <p>To add posts to a highlights feed, simply create the feed (if it's not yet created), navigate to the post
+    that you want to add and click the "Add to a highlights feed ..." link that should appear there.</p>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">9. Feeds portlet</h2>
+            </div>
+        </div>
+    </div>
+    <p>You can place one or more feeds portlets on your project page. To do that, log into Labs
+    (http://labs.jboss.com), navigate to your project and click the "Edit project" button. There
+    go to "Portlet selection". Choose the portal page on which you want the portlet placed, and
+    then from the left combo box choose "blog". Click the right arrow and the portlet is then
+    added to the selected page. Finally, specify its properties:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li><span class="bold"><strong>feedName</strong></span> - id of the feed that you want to display; this is
+            the id that you input when editing feed properties, and that is also visible in URLs when viewing this
+            feed</li>
+            <li><span class="bold"><strong>numberOfPosts</strong></span> - how many headers of posts you want to display
+            </li>
+            <li><span class="bold"><strong>summaryLength</strong></span> - if a summary of post content should be shown
+            below the post headings, this should be the number of characters you'd like it to contain (200 is a sensible
+            default). If you don't want to have a summary displayed, set it to 0</li>            
+            <li><span class="bold"><strong>showDate</strong></span> - should the date of publishing the post be
+            displayed below the header</li>
+        </ul>
+    </div>
+
+    <p>The portlet always renders a number of post titles, which link to "View post" pages, optionally with a summary
+    of the post content and the publishing date of the post. At the bottom, you'll also see two links: one to the
+    "View feed" page of the selected feed, other to the home feeds page.</p>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">10. Restricted feeds</h2>
+            </div>
+        </div>
+    </div>
+    <p>If you mark a feed as <strong>restricted</strong>, it will be viewable only by persons, who are authorized to
+    view it, that is:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li>administrators of that feed</li>
+            <li>administrators of the group, to which the feed belongs</li>
+            <li>users who are granted the "view feed" role (you can set this in the security manager)</li>
+        </ul>
+    </div>
+    <p>To view a restricted feed with the browser, you'll need to log in. However, to view the ATOM xml feed, you
+    don't need to login - you just need to know the key (that makes restricted feeds more feed-reader friendly).
+    A key will be automatically generated for you, and you don't really need to manipulate it - when you'll be viewing
+    a restricted feed using the browser, the link to the ATOM xml feed will already contain the key. If, for any reason,
+    you'd like to change the key (if it was compromised, for example), simply login, click "User account" and
+    "Re-generate the security key".</p>
+</div>
+
+<div class="sect1">
+    <div class="titlepage">
+        <div>
+            <div>
+                <h2 class="title">11. Future improvements</h2>
+            </div>
+        </div>
+    </div>
+    <p>Here's some features planned for the future:</p>
+    <div class="itemizedlist">
+        <ul>
+            <li>statistics</li>
+            <li>users proposing a single post, not a whole feed</li>
+            <li>del.icio.us integration</li>
+            <li>adding more structure to authors (binding authors to users, searching for posts by a specific
+            author/user)</li>
+            <li>anything you suggest -> <a href="mailto:adam.warski at jboss.org">adam.warski at jboss.org</a></li>
+        </ul>
+    </div>
+</div>
+
+</body>
+</html>
\ No newline at end of file

Added: feeds100P26/lib/activation.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/activation.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/ant-antlr.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/ant-antlr.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/antlr-runtime.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/antlr-runtime.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/antlr.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/antlr.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/asm-attrs.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/asm-attrs.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/asm.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/asm.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/cglib-nodep.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/cglib-nodep.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/cglib.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/cglib.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/commons-beanutils.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/commons-beanutils.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/commons-collections.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/commons-collections.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/commons-configuration-1.6.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/commons-configuration-1.6.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/commons-digester.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/commons-digester.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/commons-lang-2.4.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/commons-lang-2.4.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/commons-lang.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/commons-lang.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/commons-logging.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/commons-logging.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/core.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/core.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/dbunit.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/dbunit.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/dom4j.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/dom4j.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/drools-compiler.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/drools-compiler.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/drools-core.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/drools-core.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/easymock.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/easymock.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/ehcache-1.2.3.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/ehcache-1.2.3.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/ejb-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/ejb-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/el-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/el-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/groovy-all.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/groovy-all.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/gwt-servlet.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/gwt-servlet.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/hibernate-annotations.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/hibernate-annotations.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/hibernate-commons-annotations.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/hibernate-commons-annotations.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/hibernate-entitymanager.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/hibernate-entitymanager.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/hibernate-search.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/hibernate-search.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/hibernate-validator.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/hibernate-validator.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/hibernate.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/hibernate.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/hibernate3.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/hibernate3.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/htmlcleaner1_6.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/htmlcleaner1_6.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/itext.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/itext.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/janino.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/janino.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/javassist.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/javassist.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jaxb-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jaxb-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jaxws-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jaxws-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-aop.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-aop.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-cache.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-cache.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-common-core.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-common-core.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-dependency.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-dependency.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-deployers-client-spi.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-deployers-client-spi.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-deployers-core-spi.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-deployers-core-spi.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-el.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-el.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-embedded-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-embedded-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-jmx.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-jmx.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-kernel.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-kernel.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-logging-spi.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-logging-spi.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam-debug.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam-debug.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam-gen.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam-gen.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam-ioc.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam-ioc.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam-mail.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam-mail.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam-pdf.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam-pdf.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam-remoting.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam-remoting.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam-ui.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam-ui.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-seam.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-seam.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jboss-system.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jboss-system.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jbossxb.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jbossxb.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jbpm-jpdl.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jbpm-jpdl.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jcommon.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jcommon.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jdom.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jdom.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jericho-html-2.5.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jericho-html-2.5.jar
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jfreechart.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jfreechart.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jgroups.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jgroups.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jms.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jms.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jsf-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jsf-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jsf-facelets.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jsf-facelets.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jsf-impl.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jsf-impl.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jsp-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jsp-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jsr173_api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jsr173_api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jsr181-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jsr181-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jsr250-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jsr250-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jstl.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jstl.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/jta.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/jta.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/let-tag.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/let-tag.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/log4j.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/log4j.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/lucene-core.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/lucene-core.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/mail.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/mail.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/meldware-mailapi.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/meldware-mailapi.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/meldware-mailjmx.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/meldware-mailjmx.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/mvel14.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/mvel14.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/mysql.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/mysql.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/persistence-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/persistence-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/portal-common-lib.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/portal-common-lib.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/portal-identity-lib.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/portal-identity-lib.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/portal-portlet-jsr168api-lib.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/portal-portlet-jsr168api-lib.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/portlet-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/portlet-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/richfaces-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/richfaces-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/richfaces-impl.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/richfaces-impl.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/richfaces-ui.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/richfaces-ui.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/rome-1.0RC1.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/rome-1.0RC1.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/saaj-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/saaj-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/servlet-api.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/servlet-api.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/shotoku-base.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/shotoku-base.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/spring.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/spring.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-debug-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-debug-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-gen-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-gen-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-ioc-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-ioc-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-mail-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-mail-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-pdf-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-pdf-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-remoting-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-remoting-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/src/jboss-seam-ui-sources.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/src/jboss-seam-ui-sources.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/standard.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/standard.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/test/hibernate-all.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/test/hibernate-all.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/test/jboss-embedded-all.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/test/jboss-embedded-all.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/test/thirdparty-all.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/test/thirdparty-all.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/testng.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/testng.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/urlrewritefilter.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/urlrewritefilter.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/velocity-1.5.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/velocity-1.5.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/lib/velocity-dep-1.5.jar
===================================================================
(Binary files differ)


Property changes on: feeds100P26/lib/velocity-dep-1.5.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/resources/META-INF/application.xml
===================================================================
--- feeds100P26/resources/META-INF/application.xml	                        (rev 0)
+++ feeds100P26/resources/META-INF/application.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<application xmlns="http://java.sun.com/xml/ns/javaee"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"
+             version="5">
+
+    <display-name>blog</display-name>
+
+    <!-- Seam and EL -->
+    <module>
+        <ejb>jboss-seam.jar</ejb>
+    </module>
+
+    <module>
+        <ejb>blog-impl.jar</ejb>
+    </module>
+
+    <module>
+        <ejb>blog-shotoku.jar</ejb>
+    </module>
+
+    <module>
+        <ejb>blog-portal.jar</ejb>
+    </module>
+
+    <module>
+        <web>
+            <web-uri>blog.war</web-uri>
+            <context-root>/feeds</context-root>
+        </web>
+    </module>
+
+    <module>
+        <web>
+            <web-uri>blog-portal.war</web-uri>
+            <context-root>/blog-portal</context-root>
+        </web>
+    </module>
+</application>

Added: feeds100P26/resources/META-INF/ejb-jar.xml
===================================================================
--- feeds100P26/resources/META-INF/ejb-jar.xml	                        (rev 0)
+++ feeds100P26/resources/META-INF/ejb-jar.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" 
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
+         version="3.0">
+         
+   <interceptors>
+      <interceptor>
+         <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
+      </interceptor>
+   </interceptors>
+   
+   <assembly-descriptor>
+      <interceptor-binding>
+         <ejb-name>*</ejb-name>
+         <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class>
+      </interceptor-binding>
+   </assembly-descriptor>
+   
+</ejb-jar>
\ No newline at end of file

Added: feeds100P26/resources/META-INF/jboss-app.xml
===================================================================
--- feeds100P26/resources/META-INF/jboss-app.xml	                        (rev 0)
+++ feeds100P26/resources/META-INF/jboss-app.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE jboss-app
+        PUBLIC "-//JBoss//DTD J2EE Application 4.2//EN"
+        "http://www.jboss.org/j2ee/dtd/jboss-app_4_2.dtd">
+
+<jboss-app>
+    <loader-repository>
+        seam.jboss.org:loader=blog
+    </loader-repository>
+</jboss-app> 
\ No newline at end of file

Added: feeds100P26/resources/META-INF/jbossblog.taglib.xml
===================================================================
--- feeds100P26/resources/META-INF/jbossblog.taglib.xml	                        (rev 0)
+++ feeds100P26/resources/META-INF/jbossblog.taglib.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!DOCTYPE facelet-taglib PUBLIC
+        "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
+        "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
+<facelet-taglib>
+    <namespace>http://jboss.org/blog/tags</namespace>
+
+    <tag>
+        <tag-name>uniqueFeedNameValidator</tag-name>
+        <validator>
+            <validator-id>uniqueFeedNameValidator</validator-id>
+        </validator>
+    </tag>
+    <tag>
+        <tag-name>uniqueTemplateNameValidator</tag-name>
+        <validator>
+            <validator-id>uniqueTemplateNameValidator</validator-id>
+        </validator>
+    </tag>
+    <tag>
+        <tag-name>uniqueGroupNameValidator</tag-name>
+        <validator>
+            <validator-id>uniqueGroupNameValidator</validator-id>
+        </validator>
+    </tag>
+</facelet-taglib>
\ No newline at end of file

Added: feeds100P26/resources/META-INF/persistence-design.xml
===================================================================
--- feeds100P26/resources/META-INF/persistence-design.xml	                        (rev 0)
+++ feeds100P26/resources/META-INF/persistence-design.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Persistence deployment descriptor for dev profile -->
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+             version="1.0">
+
+    <persistence-unit name="blog">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+        <jta-data-source>java:/blogDatasource</jta-data-source>
+        <class>org.jboss.blog.model.Group</class>
+        <class>org.jboss.blog.model.feed.Feed</class>
+        <class>org.jboss.blog.model.feed.RemoteFeed</class>
+        <class>org.jboss.blog.model.feed.AggregatedFeed</class> 
+        <class>org.jboss.blog.model.feed.HighlightsFeed</class>
+        <class>org.jboss.blog.model.feed.IndividualPostsFeed</class>
+        <class>org.jboss.blog.model.feed.IndividualPostInfo</class>
+        <class>org.jboss.blog.model.Category</class>
+        <class>org.jboss.blog.model.Post</class>
+        <class>org.jboss.blog.model.Enclosure</class>
+        <class>org.jboss.blog.model.Image</class>
+        <class>org.jboss.blog.model.Template</class>
+        <class>org.jboss.blog.model.configuration.Configuration</class>
+        <class>org.jboss.blog.model.security.SecurityMapping</class>
+        <class>org.jboss.blog.model.security.SecurityGroup</class>
+        <class>org.jboss.blog.model.security.SecurityUser</class>
+        <class>org.jboss.blog.model.shotoku.ShotokuFeed</class>
+        <class>org.jboss.blog.model.log.PropositionsLog</class>
+        <properties>
+            <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
+            <property name="hibernate.hbm2ddl.auto" value="update"/>
+            <property name="hibernate.show_sql" value="false"/>
+            <property name="hibernate.format_sql" value="false"/>
+            <property name="jboss.entity.manager.factory.jndi.name" value="java:/blogEntityManagerFactory"/>
+            <property name="hibernate.connection.useUnicode" value="true" />
+            <property name="hibernate.connection.characterEncoding" value="UTF-8" />
+
+            <property name="hibernate.jdbc.batch_size" value="0" />
+
+            <property name="hibernate.cache.use_query_cache" value="true"/>
+            <property name="hibernate.cache.use_second_level_cache" value="true"/>
+            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />
+            <property name="hibernate.cache.provider_configuration_file_resource_path" value="blog-ehcache.xml" />
+            
+            <!-- TODO Search -->
+            <!-- use a file system based index
+            <property name="hibernate.search.default.directory_provider"
+                      value="org.hibernate.search.store.FSDirectoryProvider"/> -->
+            <!-- directory where the indexes will be stored
+            <property name="hibernate.search.default.indexBase"
+                      value="/Users/adamwarski/jboss/blog-index"/>
+
+            <property name="hibernate.ejb.event.post-insert"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+            <property name="hibernate.ejb.event.post-update"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+            <property name="hibernate.ejb.event.post-delete"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>-->
+        </properties>
+    </persistence-unit>
+
+</persistence>

Added: feeds100P26/resources/META-INF/persistence-dev.xml
===================================================================
--- feeds100P26/resources/META-INF/persistence-dev.xml	                        (rev 0)
+++ feeds100P26/resources/META-INF/persistence-dev.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Persistence deployment descriptor for dev profile -->
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+             version="1.0">
+    <persistence-unit name="blog">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+        <jta-data-source>java:/blogDatasource</jta-data-source>
+        <class>org.jboss.blog.model.Group</class>
+        <class>org.jboss.blog.model.feed.Feed</class>
+        <class>org.jboss.blog.model.feed.RemoteFeed</class>
+        <class>org.jboss.blog.model.feed.AggregatedFeed</class>
+        <class>org.jboss.blog.model.feed.HighlightsFeed</class>        
+        <class>org.jboss.blog.model.feed.IndividualPostsFeed</class>
+        <class>org.jboss.blog.model.feed.IndividualPostInfo</class>
+        <class>org.jboss.blog.model.Category</class>
+        <class>org.jboss.blog.model.Post</class>
+        <class>org.jboss.blog.model.Enclosure</class>
+        <class>org.jboss.blog.model.Image</class>
+        <class>org.jboss.blog.model.Template</class>
+        <class>org.jboss.blog.model.configuration.Configuration</class>
+        <class>org.jboss.blog.model.security.SecurityMapping</class>
+        <class>org.jboss.blog.model.security.SecurityGroup</class>
+        <class>org.jboss.blog.model.security.SecurityUser</class>
+        <class>org.jboss.blog.model.shotoku.ShotokuFeed</class>
+        <class>org.jboss.blog.model.log.PropositionsLog</class>
+        <properties>
+            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
+            <property name="hibernate.hbm2ddl.auto" value="update"/>
+            <property name="hibernate.show_sql" value="false"/>
+            <property name="hibernate.format_sql" value="false"/>
+            <property name="jboss.entity.manager.factory.jndi.name" value="java:/blogEntityManagerFactory"/>
+            <property name="hibernate.connection.useUnicode" value="true" />
+            <property name="hibernate.connection.characterEncoding" value="UTF-8" />
+
+            <property name="hibernate.cache.use_query_cache" value="true"/>
+            <property name="hibernate.cache.use_second_level_cache" value="true"/>
+            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />
+            <property name="hibernate.cache.provider_configuration_file_resource_path" value="blog-ehcache.xml" />
+
+            <!-- use a file system based index -->
+            <property name="hibernate.search.default.directory_provider"
+                      value="org.hibernate.search.store.FSDirectoryProvider"/>
+            <!-- directory where the indexes will be stored -->
+            <property name="hibernate.search.default.indexBase"
+                      value="/Users/adamwarski/jboss/blog-index"/>
+
+            <property name="hibernate.ejb.event.post-insert"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+            <property name="hibernate.ejb.event.post-update"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+            <property name="hibernate.ejb.event.post-delete"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+        </properties>
+    </persistence-unit>
+</persistence>

Added: feeds100P26/resources/META-INF/persistence-prod.xml
===================================================================
--- feeds100P26/resources/META-INF/persistence-prod.xml	                        (rev 0)
+++ feeds100P26/resources/META-INF/persistence-prod.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Persistence deployment descriptor for prod profile -->
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+             version="1.0">
+    <persistence-unit name="blog">
+        <provider>org.hibernate.ejb.HibernatePersistence</provider>
+        <jta-data-source>java:/blogDatasource</jta-data-source>
+        <class>org.jboss.blog.model.Group</class>
+        <class>org.jboss.blog.model.feed.Feed</class>
+        <class>org.jboss.blog.model.feed.RemoteFeed</class>
+        <class>org.jboss.blog.model.feed.AggregatedFeed</class>
+        <class>org.jboss.blog.model.feed.HighlightsFeed</class>
+        <class>org.jboss.blog.model.feed.IndividualPostsFeed</class>
+        <class>org.jboss.blog.model.feed.IndividualPostInfo</class>
+        <class>org.jboss.blog.model.Category</class>
+        <class>org.jboss.blog.model.Post</class>
+        <class>org.jboss.blog.model.Enclosure</class>
+        <class>org.jboss.blog.model.Image</class>
+        <class>org.jboss.blog.model.Template</class>
+        <class>org.jboss.blog.model.configuration.Configuration</class>
+        <class>org.jboss.blog.model.security.SecurityMapping</class>
+        <class>org.jboss.blog.model.security.SecurityGroup</class>
+        <class>org.jboss.blog.model.security.SecurityUser</class>
+        <class>org.jboss.blog.model.shotoku.ShotokuFeed</class>
+        <class>org.jboss.blog.model.log.PropositionsLog</class>
+        <properties>
+            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
+            <property name="hibernate.hbm2ddl.auto" value="update"/>
+            <property name="hibernate.show_sql" value="false"/>
+            <property name="hibernate.format_sql" value="false"/>
+            <property name="jboss.entity.manager.factory.jndi.name" value="java:/blogEntityManagerFactory"/>
+            <property name="hibernate.connection.useUnicode" value="true" />
+            <property name="hibernate.connection.characterEncoding" value="UTF-8" />
+
+            <property name="hibernate.cache.use_query_cache" value="true"/>
+            <property name="hibernate.cache.use_second_level_cache" value="true"/>
+            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />
+            <property name="hibernate.cache.provider_configuration_file_resource_path" value="blog-ehcache.xml" />
+
+            <!-- use a file system based index -->
+            <property name="hibernate.search.default.directory_provider"
+                      value="org.hibernate.search.store.FSDirectoryProvider"/>
+            <!-- directory where the indexes will be stored -->
+            <property name="hibernate.search.default.indexBase"
+                      value="/tmp/blog-index"/>
+
+            <property name="hibernate.ejb.event.post-insert"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+            <property name="hibernate.ejb.event.post-update"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+            <property name="hibernate.ejb.event.post-delete"
+                      value="org.hibernate.search.event.FullTextIndexEventListener"/>
+        </properties>
+    </persistence-unit>
+</persistence>

Added: feeds100P26/resources/META-INF/security.drl
===================================================================
--- feeds100P26/resources/META-INF/security.drl	                        (rev 0)
+++ feeds100P26/resources/META-INF/security.drl	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,207 @@
+package FeedsPermissions;
+
+import java.util.ArrayList;
+
+import org.jboss.seam.security.PermissionCheck;
+import org.jboss.seam.security.Role;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.security.FeedsSecurityRole;
+import org.jboss.blog.session.security.FeedsCombinedRole;
+
+rule CanDoAnything
+when
+  c: PermissionCheck()
+  FeedsCombinedRole(role == FeedsSecurityRole.ADMIN)
+then
+  c.grant();
+end;
+
+rule CanAddFeed
+when
+  c: PermissionCheck(name == "feed", action == "add") and
+  group : Group() and
+  FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+then
+  c.grant();
+end;
+
+rule CanProposeFeed
+when
+  c: PermissionCheck(name == "feed", action == "add") and
+  Feed(accepted == false)
+then
+  c.grant();
+end;
+
+rule CanEditFeed
+when
+  c: PermissionCheck(name == "feed", action == "edit") and
+  (
+    (
+        group : Group() and
+        feed : Feed(group == group) and
+        FeedsCombinedRole(role == FeedsSecurityRole.FEED_ADMIN, id == feed.id)
+    ) or
+    (
+        group : Group() and
+        FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+    )
+  )
+then
+  c.grant();
+end;
+
+rule CanDeleteUnacceptedFeed
+when
+  c: PermissionCheck(name == "feed", action == "delete") and
+  Feed(accepted == false) and
+  group : Group() and
+  FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+then
+  c.grant();
+end;
+
+rule CanAddGroup
+when
+  c: PermissionCheck(name == "group", action == "add") and
+  FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN)
+then
+  c.grant();
+end;
+
+rule CanEditGroup
+when
+  c: PermissionCheck(name == "group", action == "edit") and
+  group : Group() and
+  FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+then
+  c.grant();
+end;
+
+// Security-management
+
+rule CanAddOrDeleteSecurityGroupOrUser
+when
+  (
+    c: PermissionCheck(name == "security_group", action == "add") or
+    c: PermissionCheck(name == "security_user", action == "add") or
+    c: PermissionCheck(name == "security_group", action == "delete") or
+    c: PermissionCheck(name == "security_user", action == "delete")
+  ) and
+  (
+    (
+        role : FeedsSecurityRole(this == FeedsSecurityRole.FEED_ADMIN) and
+        feed : Feed() and
+        group : Group() from feed.group and
+        FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+    ) or
+    (
+        role : FeedsSecurityRole(this == FeedsSecurityRole.GROUP_ADMIN) and
+        group : Group() and
+        FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+    ) or
+    (
+        role : FeedsSecurityRole(this == FeedsSecurityRole.VIEW) and
+        feed : Feed() and
+        FeedsCombinedRole(role == FeedsSecurityRole.VIEW, id == feed.id)
+    )
+  )
+then
+  c.grant();
+end;
+
+// View-related rules
+
+rule CanViewGroupsManagement
+when
+  c: PermissionCheck(name == "management_groups", action == "view") and
+  FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN)
+then
+  c.grant();
+end;
+
+rule CanViewGroupManagement
+when
+  c: PermissionCheck(name == "management_group", action == "view") and
+  (
+    (
+        feeds : ArrayList() and
+        feed : Feed() from feeds and
+        FeedsCombinedRole(role == FeedsSecurityRole.FEED_ADMIN, id == feed.id)
+    ) or
+    (
+        group : Group() and
+        FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+    )
+  )
+then
+  c.grant();
+end;
+
+rule CanViewSecurity
+when
+  c: PermissionCheck(name == "security", action == "view") and
+  (
+    FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN)
+  )
+then
+  c.grant();
+end;
+
+rule CanViewManagement
+when
+  c: PermissionCheck(name == "management", action == "view") and
+  (
+    FeedsCombinedRole(role == FeedsSecurityRole.FEED_ADMIN) or
+    FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN)
+  )
+then
+  c.grant();
+end;
+
+rule CanViewUserAccount
+when
+  c: PermissionCheck(name == "useraccount", action == "view") and
+  FeedsCombinedRole(role == FeedsSecurityRole.VIEW)
+then
+  c.grant();
+end;
+
+rule CanAddAnyFeed
+when
+  c: PermissionCheck(name == "feed", action == "add_any") and
+  FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN)
+then
+  c.grant();
+end;
+
+// Feed-viewing
+
+rule CanViewFeed
+when
+  c: PermissionCheck(name == "feed", action == "view") and
+  (
+    Feed(feedRestricted == false) or
+    (
+      feed : Feed(feedRestricted == true) and
+      FeedsCombinedRole(role == FeedsSecurityRole.VIEW, id == feed.id)
+    )
+  )
+then
+  c.grant();
+end;
+
+rule CanViewFeedWhenFeedAmin
+when
+  c: PermissionCheck(name == "feed", action == "view") and
+  feed : Feed(feedRestricted == true) and
+  group : Group() from feed.group and
+  (
+    FeedsCombinedRole(role == FeedsSecurityRole.FEED_ADMIN, id == feed.id) or
+    FeedsCombinedRole(role == FeedsSecurityRole.GROUP_ADMIN, id == group.id)
+  )
+then
+  c.grant();
+end;
\ No newline at end of file

Added: feeds100P26/resources/WEB-INF/components.xml
===================================================================
--- feeds100P26/resources/WEB-INF/components.xml	                        (rev 0)
+++ feeds100P26/resources/WEB-INF/components.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<components xmlns="http://jboss.com/products/seam/components"
+            xmlns:core="http://jboss.com/products/seam/core"
+            xmlns:persistence="http://jboss.com/products/seam/persistence"
+            xmlns:async="http://jboss.com/products/seam/async"
+            xmlns:drools="http://jboss.com/products/seam/drools"
+            xmlns:bpm="http://jboss.com/products/seam/bpm"
+            xmlns:security="http://jboss.com/products/seam/security"
+            xmlns:web="http://jboss.com/products/seam/web"
+            xmlns:navigation="http://jboss.com/products/seam/navigation"
+            xmlns:mail="http://jboss.com/products/seam/mail"
+            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+            xsi:schemaLocation=
+                    "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd
+                 http://jboss.com/products/seam/async http://jboss.com/products/seam/async-2.0.xsd
+                 http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.0.xsd 
+                 http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.0.xsd
+                 http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.0.xsd
+                 http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.0.xsd
+                 http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd
+                 http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.0.xsd
+                 http://jboss.com/products/seam/navigation http://jboss.com/products/seam/navigation-2.0.xsd
+                 http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd">
+
+    <core:init debug="@debug@" jndi-pattern="@jndiPattern@"/>
+
+    <core:manager concurrent-request-timeout="500"
+                  conversation-timeout="300000"
+                  conversation-id-parameter="cid"/>
+
+    <persistence:managed-persistence-context name="entityManager"
+                                             auto-create="true"
+                                             persistence-unit-jndi-name="java:/blogEntityManagerFactory"/>
+
+    <security:identity authenticate-method="#{authenticator.authenticate}"
+                       security-rules="#{securityRules}" />
+
+    <async:thread-pool-dispatcher />
+
+    <drools:rule-base name="securityRules">
+        <drools:rule-files>
+            <value>/META-INF/security.drl</value>
+        </drools:rule-files>
+    </drools:rule-base>
+
+    <event type="org.jboss.seam.notLoggedIn">
+        <action execute="#{redirect.captureCurrentView}"/>
+    </event>
+    <event type="org.jboss.seam.postAuthenticate">
+        <action execute="#{redirect.returnToCapturedView}"/>
+    </event>
+
+    <event type="org.jboss.blog.captureView">
+        <action execute="#{redirect.captureCurrentView}"/>
+    </event>
+    <event type="org.jboss.blog.restoreView">
+        <action execute="#{redirect.returnToCapturedView}"/>
+    </event>
+
+    <mail:mail-session host="localhost" port="25" />
+
+    <web:context-filter url-pattern="/feeds.seam" />
+
+    <navigation:pages https-port="8443" http-port="8080" />
+
+    <component name="linkService">
+        <property name="serverAddress">http://localhost:8080</property>
+        <property name="contextName">feeds</property>
+    </component>
+</components>

Added: feeds100P26/resources/WEB-INF/faces-config.xml
===================================================================
--- feeds100P26/resources/WEB-INF/faces-config.xml	                        (rev 0)
+++ feeds100P26/resources/WEB-INF/faces-config.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<faces-config version="1.2"
+              xmlns="http://java.sun.com/xml/ns/javaee"
+              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
+
+   <application>
+      <locale-config>
+    		<default-locale>en</default-locale>
+      </locale-config>
+      <view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
+   </application>
+
+</faces-config>

Added: feeds100P26/resources/WEB-INF/pages.xml
===================================================================
--- feeds100P26/resources/WEB-INF/pages.xml	                        (rev 0)
+++ feeds100P26/resources/WEB-INF/pages.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,483 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<pages xmlns="http://jboss.com/products/seam/pages"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd"
+       no-conversation-view-id="/home.xhtml"
+       login-view-id="/security/login.xhtml">
+
+    <!-- Global: security and group mod -->
+
+    <page view-id="*" scheme="http">
+        <navigation from-action="#{identity.logout}">
+            <redirect view-id="/home.xhtml"/>
+        </navigation>
+        <navigation from-action="#{groupMod.add}">
+            <begin-conversation nested="true" flush-mode="manual" />
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/group/group_add.xhtml" />
+        </navigation>
+        <navigation from-action="#{groupMod.cancel}">
+            <end-conversation />
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+    </page>
+
+    <!-- Security -->
+
+    <page view-id="/security/login.xhtml" scheme="https">
+        <navigation from-action="#{identity.login}">
+            <rule if="#{identity.loggedIn}">
+                <redirect view-id="/home.xhtml"/>
+            </rule>
+        </navigation>
+    </page>
+
+    <page view-id="/security/account.xhtml" login-required="true" />
+
+    <!-- View feeds -->
+
+    <page view-id="/home.xhtml" />
+
+    <page view-id="/view/feed.xhtml">
+        <param name="name" converterId="feedConverter" value="#{feedView.feed}" />
+        <param name="from" value="#{feedView.from}" />
+
+        <restrict>#{identity.hasPermission('feed', 'view', feedView.feed)}</restrict>
+    </page>
+
+    <page view-id="/view/post.xhtml">
+        <param name="post" converterId="postConverter" value="#{postView.post}" />
+        <navigation from-action="#{postView.delete}">
+            <redirect view-id="/view/feed.xhtml">
+                <param name="name" value="#{postView.post.feed.name}" />
+            </redirect>
+        </navigation>
+
+        <restrict>#{identity.hasPermission('feed', 'view', postView.post.feed)}</restrict>
+    </page>
+
+    <!-- Search -->
+
+    <page view-id="/search/search.xhtml">
+        <param name="query" value="#{postSearch.query}" />
+        <param name="from" value="#{postSearch.from}" />
+
+        <action execute="#{postSearch.search}" />
+    </page>
+
+    <!-- Manage main -->
+
+    <page view-id="/manage/index.xhtml" login-required="true">
+        <restrict>#{identity.hasPermission('management', 'view')}</restrict>
+    </page>
+
+    <!-- Manage feeds -->
+
+    <page view-id="/manage/feed_add.xhtml" conversation-required="true" login-required="true">
+        <navigation from-action="#{feedMod.saveNew}">
+            <end-conversation />
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/feed_propose.xhtml" conversation-required="true" login-required="true">
+        <navigation from-action="#{feedMod.saveNew}">
+            <end-conversation />
+            <raise-event type="org.jboss.blog.feed.proposed" />
+            <redirect view-id="/home.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/feed_edit.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{feedMod.saveExisting}">
+            <end-conversation />
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/feed_delete.xhtml" login-required="true">
+        <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+        <restrict>#{identity.hasPermission('feed', 'delete', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{feedMod.delete}">
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+    </page>
+
+    <!-- Manage propositions -->
+
+    <page view-id="/manage/proposition/proposition_accept_1.xhtml" login-required="true">
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+        <navigation from-action="#{remoteFeedMod.saveExisting}">
+            <redirect view-id="/manage/proposition/proposition_accept_2.xhtml" />
+        </navigation>
+        <navigation from-action="#{remoteFeedMod.savePartial}">
+            <redirect view-id="/manage/proposition/proposition_accept_2.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/proposition/proposition_accept_2.xhtml" login-required="true" conversation-required="true">
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{feedMod.saveExisting}">
+            <raise-event type="org.jboss.blog.feed.accept" />
+            <end-conversation />
+            <redirect view-id="/manage/proposition/proposition_list.xhtml" />
+        </navigation>
+    </page>
+
+    <!-- Manage remote feeds -->
+
+    <page view-id="/manage/remote/remote_add.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <navigation from-action="#{remoteFeedMod.saveNew}">
+            <redirect view-id="/manage/feed_add.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/remote/remote_edit.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{remoteFeedMod.saveExisting}">
+            <end-conversation />
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+        <navigation from-action="#{remoteFeedMod.savePartial}">
+            <end-conversation />
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/remote/remote_propose.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <action execute="#{remoteFeedMod.unsetAccepted}" />
+        <navigation from-action="#{remoteFeedMod.saveNew}">
+            <redirect view-id="/manage/feed_propose.xhtml" />
+        </navigation>
+    </page>
+
+    <!-- Manage aggregated feeds -->
+
+    <page view-id="/manage/aggregated/aggregated_add.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <navigation from-action="#{aggregatedFeedMod.saveNew}">
+            <redirect view-id="/manage/feed_add.xhtml" />
+        </navigation>
+
+        <!-- Should be done using a wildcard, but they aren't supported -->
+        <navigation from-action="#{filterAdd.addToList(aggregatedFeedMod.globalFilters)}">
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/aggregated/filter_add.xhtml" />
+        </navigation>
+        <navigation from-action="#{filterAdd.addToList(aggregatedFeedMod.selectedFeedsFilters[feed])}">
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/aggregated/filter_add.xhtml" />
+        </navigation>
+        <navigation from-action="#{filterAdd.addToList(aggregatedFeedMod.selectedGroupsFilters[group])}">
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/aggregated/filter_add.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/aggregated/aggregated_edit.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{aggregatedFeedMod.saveExisting}">
+            <end-conversation />
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+
+        <!-- Should be done using a wildcard, but they aren't supported -->
+        <navigation from-action="#{filterAdd.addToList(aggregatedFeedMod.globalFilters)}">
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/aggregated/filter_add.xhtml" />
+        </navigation>
+        <navigation from-action="#{filterAdd.addToList(aggregatedFeedMod.selectedFeedsFilters[feed])}">
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/aggregated/filter_add.xhtml" />
+        </navigation>
+        <navigation from-action="#{filterAdd.addToList(aggregatedFeedMod.selectedGroupsFilters[group])}">
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/aggregated/filter_add.xhtml" />
+        </navigation>
+    </page>
+
+    <!-- Add filters -->
+
+    <page view-id="/manage/aggregated/filter_add.xhtml" conversation-required="true" login-required="true">
+        <!-- Should be done using a wildcard, but they aren't supported -->
+        <navigation from-action="#{filterAdd.add(filterAdd.podcastFilter)}">
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+        <navigation from-action="#{filterAdd.add(filterAdd.notPodcastFilter)}">
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+        <navigation from-action="#{filterAdd.add(filterAdd.authorRegexpFilter)}">
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+        <navigation from-action="#{filterAdd.add(filterAdd.categoryRegexpFilter)}">
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+        <navigation from-action="#{filterAdd.cancel}">
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+    </page>
+
+    <!-- Manage highlights feeds -->
+
+    <page view-id="/manage/highlights/highlights_add.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <action execute="#{highlightsFeedMod.getHighlightsFeed}"/>
+
+        <navigation>
+            <redirect view-id="/manage/feed_add.xhtml">
+                <message severity="INFO">#{messages['blog.feed.highlights.adding.info']}</message>
+            </redirect>
+        </navigation>
+    </page>
+
+    <page view-id="/manage/highlights/highlights_edit.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{highlightsFeedMod.saveExisting}">
+            <end-conversation />
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/highlights/post_add.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="post" converterId="postConverter" value="#{highlightsFeedMod.post}" />
+        <restrict>#{identity.hasPermission('management', 'view')}</restrict>
+        <navigation from-action="#{highlightsFeedMod.addPost}">
+            <end-conversation />
+            <redirect view-id="/manage/highlights/highlights_edit.xhtml">
+                <param name="name" value="#{highlightsFeedMod.selectedFeed.name}" />
+            </redirect>
+        </navigation>
+    </page>
+
+    <!-- Manage individual posts feeds -->
+
+    <page view-id="/manage/individual/individual_add.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <!-- Initializing the new feed, if not yet initialized -->
+        <action execute="#{individualFeedMod.getIndividualPostsFeed}" />
+        <navigation from-action="#{feedMod.saveNew}">
+            <end-conversation before-redirect="true" />
+            <redirect view-id="/manage/individual/individual_edit.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/individual/individual_edit.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+        <param name="from" value="#{individualFeedMod.from}" />
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{individualFeedMod.saveExisting}">
+            <end-conversation />
+            <redirect view-id="/manage/index.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/individual/post_add.xhtml" login-required="true" conversation-required="true">
+        <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+        <navigation from-action="#{individualFeedMod.addPost}">
+            <redirect view-id="/manage/individual/individual_edit.xhtml" />
+        </navigation>
+        <navigation from-action="#{individualFeedMod.reset}">
+            <redirect view-id="/manage/individual/individual_edit.xhtml" />
+        </navigation>
+    </page>
+
+    <!-- Manage groups -->
+
+    <page view-id="/manage/group/group_list.xhtml" login-required="true">
+        <restrict>#{identity.hasPermission('management_groups', 'view')}</restrict>
+        <param name="id" converterId="groupConverter" value="#{groupMod.group}" />
+        <navigation from-action="#{groupMod.edit}">
+            <begin-conversation nested="true" flush-mode="manual" />
+            <raise-event type="org.jboss.blog.captureView" />
+            <redirect view-id="/manage/group/group_edit.xhtml">
+                <param name="id" converterId="groupConverter" value="#{groupMod.group}" />
+            </redirect>
+        </navigation>
+    </page>
+
+    <page view-id="/manage/group/group_add.xhtml" login-required="true">
+        <restrict>#{identity.hasPermission('group', 'add')}</restrict>
+        <navigation from-action="#{groupMod.saveNew}">
+            <end-conversation />
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/group/group_edit.xhtml" login-required="true">
+        <param name="id" converterId="groupConverter" value="#{groupMod.group}" />
+        <restrict>#{identity.hasPermission('group', 'edit', groupMod.group)}</restrict>
+        <navigation from-action="#{groupMod.saveExisting}">
+            <end-conversation />
+            <raise-event type="org.jboss.blog.restoreView" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/group/group_delete.xhtml" login-required="true">
+        <param name="id" converterId="groupConverter" value="#{groupMod.group}" />
+        <restrict>#{identity.hasPermission('group', 'delete', groupMod.group)}</restrict>
+        <navigation from-action="#{groupMod.delete}">
+            <redirect view-id="/manage/group/group_list.xhtml" />
+        </navigation>
+    </page>
+
+    <!-- Manage templates -->
+
+    <page view-id="/manage/template/template_list.xhtml" login-required="true">
+        <restrict>#{identity.hasPermission('management_template', 'view')}</restrict>
+    </page>
+
+    <page view-id="/manage/template/template_add.xhtml" login-required="true">
+        <restrict>#{identity.hasPermission('template', 'add')}</restrict>
+        <begin-conversation flush-mode="manual" join="true" />
+        <navigation from-action="#{templateMod.saveNew}">
+            <end-conversation />
+            <redirect view-id="/manage/template/template_list.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/template/template_edit.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <param name="id" converterId="templateConverter" value="#{templateMod.template}" />
+        <restrict>#{identity.hasPermission('template', 'edit', templateMod.template)}</restrict>
+        <navigation from-action="#{templateMod.saveExisting}">
+            <end-conversation />
+            <redirect view-id="/manage/template/template_list.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/manage/template/template_delete.xhtml" login-required="true">
+        <param name="id" converterId="templateConverter" value="#{templateMod.template}" />
+        <restrict>#{identity.hasPermission('template', 'delete', templateMod.template)}</restrict>
+        <navigation from-action="#{templateMod.delete}">
+            <redirect view-id="/manage/template/template_list.xhtml" />
+        </navigation>
+    </page>
+
+    <!-- Manage updates -->
+
+    <page view-id="/manage/update_manager.xhtml" login-required="true">
+        <restrict>#{identity.hasPermission('admin', '')}</restrict>
+    </page>
+
+    <!-- Manage configuration -->
+
+    <page view-id="/manage/configuration_manager.xhtml" login-required="true">
+        <begin-conversation flush-mode="manual" join="true" />
+        <restrict>#{identity.hasPermission('admin', '')}</restrict>
+    </page>
+
+    <!-- Manage security -->
+
+    <page view-id="/security/security_manager.xhtml">
+        <restrict>#{identity.hasPermission('security', 'view')}</restrict>
+        <param name="securityGroup" converterId="securityGroupConverter" value="#{securityMod.restrictedSecurityGroup}" />
+        <param name="securityUser" converterId="securityUserConverter" value="#{securityMod.restrictedSecurityUser}" />
+        <param name="group" converterId="groupConverter" value="#{securityMod.group}" />
+        <param name="feed" converterId="feedConverter" value="#{securityMod.feed}" />
+        <param name="role" converterId="securityRoleConverter" value="#{securityMod.role}" />
+    </page>
+
+    <page view-id="/security/security_group_add.xhtml">
+        <restrict>#{identity.hasPermission('security', 'view')}</restrict>
+        <param name="group" converterId="groupConverter" value="#{securityMod.group}" />
+        <param name="feed" converterId="feedConverter" value="#{securityMod.feed}" />
+        <param name="role" converterId="securityRoleConverter" value="#{securityMod.role}" />
+
+        <navigation from-action="#{securityMod.addSecurityGroup}">
+            <redirect view-id="/security/security_manager.xhtml" />
+        </navigation>
+    </page>
+
+    <page view-id="/security/security_user_add.xhtml">
+        <restrict>#{identity.hasPermission('security', 'view')}</restrict>
+        <param name="group" converterId="groupConverter" value="#{securityMod.group}" />
+        <param name="feed" converterId="feedConverter" value="#{securityMod.feed}" />
+        <param name="role" converterId="securityRoleConverter" value="#{securityMod.role}" />
+
+        <param name="from" value="#{securityUserSelect.from}" />
+        <param name="filter" value="#{securityUserSelect.filter}" />
+
+        <navigation from-action="#{securityMod.addSecurityUser}">
+            <redirect view-id="/security/security_manager.xhtml" />
+        </navigation>
+
+        <navigation>
+            <rule if-outcome="search">
+                <redirect />
+            </rule>
+        </navigation>
+    </page>
+
+    <!-- Exceptions -->
+
+    <!-- TODO add in 2.0.2: log="false" -->
+    <exception class="org.jboss.blog.session.exceptions.FeedNotFoundRuntimeException">
+        <redirect view-id="/error/feed_error.xhtml">
+            <message>The requested feed hasn't been found.</message>
+        </redirect>
+    </exception>
+
+    <exception class="org.jboss.blog.session.exceptions.PostNotFoundRuntimeException">
+        <redirect view-id="/error/post_error.xhtml">
+            <message>The requested post hasn't been found.</message>
+        </redirect>
+    </exception>
+
+    <exception class="org.jboss.seam.framework.EntityNotFoundException">
+        <redirect view-id="/error/error.xhtml">
+            <message>Not found</message>
+        </redirect>
+    </exception>
+
+    <exception class="javax.persistence.EntityNotFoundException">
+        <redirect view-id="/error/error.xhtml">
+            <message>Not found</message>
+        </redirect>
+    </exception>
+
+    <exception class="javax.persistence.OptimisticLockException">
+        <end-conversation/>
+        <redirect view-id="/error/error.xhtml">
+            <message>Another user changed the same data, please try again</message>
+        </redirect>
+    </exception>
+
+    <exception class="org.jboss.seam.security.AuthorizationException">
+        <redirect view-id="/error/error.xhtml">
+            <message>You don't have permission to do this</message>
+        </redirect>
+    </exception>
+
+    <exception class="org.jboss.seam.security.NotLoggedInException">
+        <redirect view-id="/security/login.xhtml">
+            <message>Please log in first</message>
+        </redirect>
+    </exception>
+
+    <exception class="javax.faces.application.ViewExpiredException">
+        <redirect view-id="/error/error.xhtml">
+            <message>Your session has timed out, please try again</message>
+        </redirect>
+    </exception>
+
+    <exception>
+        <redirect view-id="/error/error.xhtml">
+            <message>Unexpected error, please try again</message>
+        </redirect>
+    </exception>
+</pages>

Added: feeds100P26/resources/WEB-INF/urlrewrite.xml
===================================================================
--- feeds100P26/resources/WEB-INF/urlrewrite.xml	                        (rev 0)
+++ feeds100P26/resources/WEB-INF/urlrewrite.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 2.6//EN"
+        "http://tuckey.org/res/dtds/urlrewrite2.6.dtd">
+
+<!--
+
+    Configuration file for UrlRewriteFilter
+    http://tuckey.org/urlrewrite/
+
+-->
+<urlrewrite>
+    <!-- Main view -->
+
+    <rule>
+        <from>^/index.html$</from>
+        <to>/home.seam</to>
+    </rule>
+
+    <outbound-rule>
+        <from>^(.*)/feeds/home.seam$</from>
+        <to>$1/feeds/</to>
+    </outbound-rule>
+
+    <!-- Posts view -->
+
+    <rule>
+        <from>^/post/([a-z0-9_]*)(\?.+)?$</from>
+        <to>/view/post.seam?post=$1$2</to>
+    </rule>
+
+    <outbound-rule>
+        <from>^(.*)/feeds/view/post.seam\?post=(\w+)$</from>
+        <to>$1/feeds/post/$2</to>
+    </outbound-rule>
+
+    <outbound-rule>
+        <from>^(.*)/feeds/view/post.seam\?post=(\w+)&amp;(.+)$</from>
+        <to>$1/feeds/post/$2?$3</to>
+    </outbound-rule>
+
+    <outbound-rule>
+        <from>^(.*)/feeds/view/post.seam\?cid=(\d+)&amp;post=(\w+)(.*)$</from>
+        <to>$1/feeds/post/$3?cid=$2$4</to>
+    </outbound-rule>
+
+    <!-- Feeds -->
+
+    <rule>
+        <from>^/xml/([a-z0-9_]+)(\?.+)?$</from>
+        <to>/feeds.seam?name=$1</to>
+    </rule>
+
+    <outbound-rule>
+        <from>^(.*)/feeds/feeds.seam\?type=(\w+)&amp;name=([a-z0-9_]*)$</from>
+        <to>$1/feeds/xml/$3?type=$2</to>
+    </outbound-rule>
+
+    <!-- Feed view -->
+
+    <rule>
+        <from>^/view/([a-z0-9_]+)(\?.+)?$</from>
+        <to>/view/feed.seam?name=$1$2</to>
+    </rule>
+
+    <outbound-rule>
+        <from>^(.*)/feeds/view/feed.seam\?(cid=\d+&amp;)?(from=\d+)&amp;name=([a-z0-9_]+)(.*)$</from>
+        <to>$1/feeds/view/$4?$2$3$5</to>
+    </outbound-rule>
+
+    <!-- Legacy Labs URLs -->
+
+    <rule>
+        <from>^/([a-zA-Z0-9_]+)/(rss2|atom|rdf)$</from>
+        <to type="permanent-redirect">/feeds/xml/$1?type=atom</to>
+    </rule>
+</urlrewrite>
+

Added: feeds100P26/resources/WEB-INF/web-design.xml
===================================================================
--- feeds100P26/resources/WEB-INF/web-design.xml	                        (rev 0)
+++ feeds100P26/resources/WEB-INF/web-design.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,147 @@
+<?xml version="1.0" ?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         version="2.5">
+
+    <!-- Ajax4jsf -->
+
+    <context-param>
+        <param-name>org.richfaces.SKIN</param-name>
+        <param-value>blueSky</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>sourceBasePath</param-name>
+        <param-value>/Users/adamwarski/jboss/jboss-design/server/default/deploy/blog.ear/blog.war</param-value>
+    </context-param>
+
+    <!-- URL rewrite -->
+
+    <filter>
+        <filter-name>UrlRewriteFilter</filter-name>
+        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
+        <init-param>
+            <param-name>logLevel</param-name>
+            <param-value>WARN</param-value>
+        </init-param>
+        <init-param>
+            <param-name>statusEnabled</param-name>
+            <param-value>false</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>UrlRewriteFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <!-- Seam -->
+
+    <listener>
+        <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
+    </listener>
+
+    <filter>
+        <filter-name>Seam Filter</filter-name>
+        <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Seam Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+        <dispatcher>ERROR</dispatcher>
+        <dispatcher>FORWARD</dispatcher>
+        <dispatcher>INCLUDE</dispatcher>
+        <dispatcher>REQUEST</dispatcher>
+    </filter-mapping>
+
+    <!-- Resources filter -->
+
+    <!--
+    <filter>
+        <filter-name>Resources Filter</filter-name>
+        <filter-class>org.jboss.shotoku.web.ResourcesFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Resources Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+        <dispatcher>ERROR</dispatcher>
+        <dispatcher>FORWARD</dispatcher>
+        <dispatcher>INCLUDE</dispatcher>
+        <dispatcher>REQUEST</dispatcher>
+    </filter-mapping>
+    -->
+
+    <servlet>
+        <servlet-name>Seam Resource Servlet</servlet-name>
+        <servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Seam Resource Servlet</servlet-name>
+        <url-pattern>/seam/resource/*</url-pattern>
+    </servlet-mapping>
+    <!-- Facelets development mode (disable in production) -->
+
+    <context-param>
+        <param-name>facelets.DEVELOPMENT</param-name>
+        <param-value>true</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>facelets.REFRESH_PERIOD</param-name>
+        <param-value>0</param-value>
+    </context-param>
+
+    <!--
+    <context-param>
+        <param-name>facelets.RESOURCE_RESOLVER</param-name>
+        <param-value>org.jboss.shotoku.web.FilesystemResourceResolver</param-value>
+    </context-param>
+    -->
+
+    <context-param>
+        <param-name>facelets.SKIP_COMMENTS</param-name>
+        <param-value>true</param-value>
+    </context-param>
+
+    <!-- JSF -->
+
+    <context-param>
+        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
+        <param-value>.xhtml</param-value>
+    </context-param>
+
+    <servlet>
+        <servlet-name>Faces Servlet</servlet-name>
+        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Faces Servlet</servlet-name>
+        <url-pattern>*.seam</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <display-name>Restrict raw XHTML Documents</display-name>
+        <web-resource-collection>
+            <web-resource-name>XHTML</web-resource-name>
+            <url-pattern>*.xhtml</url-pattern>
+        </web-resource-collection>
+        <auth-constraint/>
+    </security-constraint>
+
+    <!-- Feeds servlet -->
+
+    <servlet>
+        <servlet-name>Feeds Servlet</servlet-name>
+        <servlet-class>org.jboss.blog.servlet.FeedsServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Feeds Servlet</servlet-name>
+        <url-pattern>/feeds.seam</url-pattern>
+    </servlet-mapping>
+</web-app>

Added: feeds100P26/resources/WEB-INF/web-dev.xml
===================================================================
--- feeds100P26/resources/WEB-INF/web-dev.xml	                        (rev 0)
+++ feeds100P26/resources/WEB-INF/web-dev.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,143 @@
+<?xml version="1.0" ?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         version="2.5">
+
+    <!-- Ajax4jsf -->
+
+    <context-param>
+        <param-name>org.richfaces.SKIN</param-name>
+        <param-value>blueSky</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>sourceBasePath</param-name>
+        <param-value>/Users/adamwarski/blog/view</param-value>
+    </context-param>
+
+    <!-- URL rewrite -->
+
+    <filter>
+        <filter-name>UrlRewriteFilter</filter-name>
+        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
+        <init-param>
+            <param-name>logLevel</param-name>
+            <param-value>WARN</param-value>
+        </init-param>
+        <init-param>
+            <param-name>statusEnabled</param-name>
+            <param-value>false</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>UrlRewriteFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <!-- Seam -->
+
+    <listener>
+        <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
+    </listener>
+
+    <filter>
+        <filter-name>Seam Filter</filter-name>
+        <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Seam Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+        <dispatcher>ERROR</dispatcher>
+        <dispatcher>FORWARD</dispatcher>
+        <dispatcher>INCLUDE</dispatcher>
+        <dispatcher>REQUEST</dispatcher>
+    </filter-mapping>
+
+    <!-- Resources filter -->
+
+    <filter>
+        <filter-name>Resources Filter</filter-name>
+        <filter-class>org.jboss.shotoku.web.ResourcesFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Resources Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+        <dispatcher>ERROR</dispatcher>
+        <dispatcher>FORWARD</dispatcher>
+        <dispatcher>INCLUDE</dispatcher>
+        <dispatcher>REQUEST</dispatcher>
+    </filter-mapping>
+
+    <servlet>
+        <servlet-name>Seam Resource Servlet</servlet-name>
+        <servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Seam Resource Servlet</servlet-name>
+        <url-pattern>/seam/resource/*</url-pattern>
+    </servlet-mapping>
+    <!-- Facelets development mode (disable in production) -->
+
+    <context-param>
+        <param-name>facelets.DEVELOPMENT</param-name>
+        <param-value>true</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>facelets.REFRESH_PERIOD</param-name>
+        <param-value>0</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>facelets.RESOURCE_RESOLVER</param-name>
+        <param-value>org.jboss.shotoku.web.FilesystemResourceResolver</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>facelets.SKIP_COMMENTS</param-name>
+        <param-value>true</param-value>
+    </context-param>
+
+    <!-- JSF -->
+
+    <context-param>
+        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
+        <param-value>.xhtml</param-value>
+    </context-param>
+
+    <servlet>
+        <servlet-name>Faces Servlet</servlet-name>
+        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Faces Servlet</servlet-name>
+        <url-pattern>*.seam</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <display-name>Restrict raw XHTML Documents</display-name>
+        <web-resource-collection>
+            <web-resource-name>XHTML</web-resource-name>
+            <url-pattern>*.xhtml</url-pattern>
+        </web-resource-collection>
+        <auth-constraint/>
+    </security-constraint>
+
+    <!-- Feeds servlet -->
+
+    <servlet>
+        <servlet-name>Feeds Servlet</servlet-name>
+        <servlet-class>org.jboss.blog.servlet.FeedsServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Feeds Servlet</servlet-name>
+        <url-pattern>/feeds.seam</url-pattern>
+    </servlet-mapping>
+</web-app>

Added: feeds100P26/resources/WEB-INF/web-prod.xml
===================================================================
--- feeds100P26/resources/WEB-INF/web-prod.xml	                        (rev 0)
+++ feeds100P26/resources/WEB-INF/web-prod.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,139 @@
+<?xml version="1.0" ?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         version="2.5">
+
+    <!-- Ajax4jsf -->
+
+    <context-param>
+        <param-name>org.richfaces.SKIN</param-name>
+        <param-value>blueSky</param-value>
+    </context-param>
+
+    <!-- URL rewrite -->
+
+    <filter>
+        <filter-name>UrlRewriteFilter</filter-name>
+        <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
+        <init-param>
+            <param-name>logLevel</param-name>
+            <param-value>WARN</param-value>
+        </init-param>
+        <init-param>
+            <param-name>statusEnabled</param-name>
+            <param-value>false</param-value>
+        </init-param>
+    </filter>
+    <filter-mapping>
+        <filter-name>UrlRewriteFilter</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <!-- Resources filter -->
+
+    <filter>
+        <filter-name>Resources Filter</filter-name>
+        <filter-class>org.jboss.shotoku.web.ShotokuResourcesFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Resources Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+        <dispatcher>ERROR</dispatcher>
+        <dispatcher>FORWARD</dispatcher>
+        <dispatcher>INCLUDE</dispatcher>
+        <dispatcher>REQUEST</dispatcher>
+    </filter-mapping>
+
+    <!-- Seam -->
+
+    <listener>
+        <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
+    </listener>
+
+    <filter>
+        <filter-name>Seam Filter</filter-name>
+        <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Seam Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+        <dispatcher>ERROR</dispatcher>
+        <dispatcher>FORWARD</dispatcher>
+        <dispatcher>INCLUDE</dispatcher>
+        <dispatcher>REQUEST</dispatcher>
+    </filter-mapping>
+
+    <servlet>
+        <servlet-name>Seam Resource Servlet</servlet-name>
+        <servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Seam Resource Servlet</servlet-name>
+        <url-pattern>/seam/resource/*</url-pattern>
+    </servlet-mapping>
+
+    <!-- Facelets development mode (disable in production) -->
+
+    <context-param>
+        <param-name>facelets.DEVELOPMENT</param-name>
+        <param-value>true</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>facelets.REFRESH_PERIOD</param-name>
+        <param-value>0</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>facelets.RESOURCE_RESOLVER</param-name>
+        <param-value>org.jboss.shotoku.web.ShotokuFilesystemResourceResolver</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>facelets.SKIP_COMMENTS</param-name>
+        <param-value>true</param-value>
+    </context-param>
+
+    <!-- JSF -->
+
+    <context-param>
+        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
+        <param-value>.xhtml</param-value>
+    </context-param>
+
+    <servlet>
+        <servlet-name>Faces Servlet</servlet-name>
+        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Faces Servlet</servlet-name>
+        <url-pattern>*.seam</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <display-name>Restrict raw XHTML Documents</display-name>
+        <web-resource-collection>
+            <web-resource-name>XHTML</web-resource-name>
+            <url-pattern>*.xhtml</url-pattern>
+        </web-resource-collection>
+        <auth-constraint/>
+    </security-constraint>
+
+    <!-- Feeds servlet -->
+
+    <servlet>
+        <servlet-name>Feeds Servlet</servlet-name>
+        <servlet-class>org.jboss.blog.servlet.FeedsServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Feeds Servlet</servlet-name>
+        <url-pattern>/feeds.seam</url-pattern>
+    </servlet-mapping>
+</web-app>

Added: feeds100P26/resources/blog-design-ds.xml
===================================================================
--- feeds100P26/resources/blog-design-ds.xml	                        (rev 0)
+++ feeds100P26/resources/blog-design-ds.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE datasources
+    PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
+    "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
+    
+<datasources>
+   
+   <local-tx-datasource>
+      <jndi-name>blogDatasource</jndi-name>
+      <connection-url>jdbc:hsqldb:${jboss.server.data.dir}${/}hypersonic${/}localDB</connection-url>
+      <driver-class>org.hsqldb.jdbcDriver</driver-class>
+      <user-name>sa</user-name>
+      <password></password>
+   </local-tx-datasource>
+    
+</datasources>
+

Added: feeds100P26/resources/blog-dev-ds.xml
===================================================================
--- feeds100P26/resources/blog-dev-ds.xml	                        (rev 0)
+++ feeds100P26/resources/blog-dev-ds.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE datasources
+    PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
+    "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
+    
+<datasources>
+   
+   <local-tx-datasource>
+      <jndi-name>blogDatasource</jndi-name>
+      <connection-url>jdbc:mysql:///blog?useUnicode=true&amp;characterEncoding=UTF-8</connection-url>
+      <driver-class>com.mysql.jdbc.Driver</driver-class>
+      <user-name>root</user-name>
+      <password></password>
+<!-- 
+      <exception-sorter-class-name>
+         org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
+      </exception-sorter-class-name>
+      <metadata>
+         <type-mapping>mySQL</type-mapping>
+      </metadata>
+-->
+   </local-tx-datasource>
+    
+</datasources>
+

Added: feeds100P26/resources/blog-ehcache.xml
===================================================================
--- feeds100P26/resources/blog-ehcache.xml	                        (rev 0)
+++ feeds100P26/resources/blog-ehcache.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,11 @@
+<ehcache>
+    <diskStore path="java.io.tmpdir"/>
+
+    <defaultCache
+        maxElementsInMemory="10000"
+        eternal="true"
+        overflowToDisk="true"
+        diskPersistent="false"
+        diskExpiryThreadIntervalSeconds="120"
+        memoryStoreEvictionPolicy="LRU" />
+</ehcache>
\ No newline at end of file

Added: feeds100P26/resources/blog-prod-ds.xml
===================================================================
--- feeds100P26/resources/blog-prod-ds.xml	                        (rev 0)
+++ feeds100P26/resources/blog-prod-ds.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE datasources
+    PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
+    "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
+    
+<datasources>
+   
+   <local-tx-datasource>
+      <jndi-name>blogDatasource</jndi-name>
+      <connection-url>jdbc:mysql:///adam_blog?useUnicode=true&amp;characterEncoding=UTF-8</connection-url>
+      <driver-class>com.mysql.jdbc.Driver</driver-class>
+      <user-name>blog</user-name>
+      <password>blog</password>
+<!-- 
+      <exception-sorter-class-name>
+         org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
+      </exception-sorter-class-name>
+      <metadata>
+         <type-mapping>mySQL</type-mapping>
+      </metadata>
+-->
+   </local-tx-datasource>
+    
+</datasources>
+

Added: feeds100P26/resources/components.properties
===================================================================
--- feeds100P26/resources/components.properties	                        (rev 0)
+++ feeds100P26/resources/components.properties	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,2 @@
+jndiPattern \#{ejbName}/local
+debug true

Added: feeds100P26/resources/messages_en.properties
===================================================================
--- feeds100P26/resources/messages_en.properties	                        (rev 0)
+++ feeds100P26/resources/messages_en.properties	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,173 @@
+up=\u2191
+down=\u2193
+left=\u2039
+right=\u203a
+
+validator.assertFalse=validation failed
+validator.assertTrue=validation failed
+validator.future=must be a future date
+validator.length=Length must be between {min} and {max}.
+validator.max=Must be less than or equal to {value}.
+validator.min=Must be greater than or equal to {value}.
+validator.notNull=may not be null
+validator.past=must be a past date
+validator.pattern=Must match "{regex}".
+validator.range=Must be between {min} and {max}.
+validator.size=Size must be between {min} and {max}.
+validator.email=Must be a well-formed email address.
+
+org.jboss.seam.loginFailed=Login failed.
+org.jboss.seam.loginSuccessful=Welcome, #0!
+
+org.jboss.seam.TransactionFailed=Transaction failed.
+org.jboss.seam.NoConversation=The conversation ended, timed out or was processing another request.
+org.jboss.seam.IllegalNavigation=Illegal navigation.
+org.jboss.seam.ProcessEnded=Process #0 already ended.
+org.jboss.seam.ProcessNotFound=Process #0 not found.
+org.jboss.seam.TaskEnded=Task #0 already ended.
+org.jboss.seam.TaskNotFound=Task #0 not found.
+org.jboss.seam.NotLoggedIn=Please log in first.
+
+javax.faces.component.UIInput.CONVERSION=value could not be converted to the expected type
+javax.faces.component.UIInput.REQUIRED=This field cannot be empty.
+javax.faces.component.UIInput.UPDATE=an error occurred when processing your submitted information
+javax.faces.component.UISelectOne.INVALID=value is not valid
+javax.faces.component.UISelectMany.INVALID=value is not valid
+
+javax.faces.converter.BigDecimalConverter.DECIMAL=value must be a number
+javax.faces.converter.BigDecimalConverter.DECIMAL_detail=value must be a signed decimal number consisting of zero or more digits, optionally followed by a decimal point and fraction, eg. {1}
+javax.faces.converter.BigIntegerConverter.BIGINTEGER=value must be an integer
+javax.faces.converter.BigIntegerConverter.BIGINTEGER_detail=value must be a signed integer number consisting of zero or more digits
+javax.faces.converter.BooleanConverter.BOOLEAN=value must be true or false
+javax.faces.converter.BooleanConverter.BOOLEAN_detail=value must be true or false (any value other than true will evaluate to false)
+javax.faces.converter.ByteConverter.BYTE=value must be a number between 0 and 255
+javax.faces.converter.ByteConverter.BYTE_detail=value must be a number between 0 and 255
+javax.faces.converter.CharacterConverter.CHARACTER=value must be a character
+javax.faces.converter.CharacterConverter.CHARACTER_detail=value must be a valid ASCII character
+javax.faces.converter.DateTimeConverter.DATE=value must be a date
+javax.faces.converter.DateTimeConverter.DATE_detail=value must be a date,  eg. {1}
+javax.faces.converter.DateTimeConverter.TIME=value must be a time
+javax.faces.converter.DateTimeConverter.TIME_detail=value must be a time,  eg. {1}
+javax.faces.converter.DateTimeConverter.DATETIME=value must be a date and time
+javax.faces.converter.DateTimeConverter.DATETIME_detail=value must be a date and time,  eg. {1}
+javax.faces.converter.DateTimeConverter.PATTERN_TYPE=a pattern or type attribute must be specified to convert the value
+javax.faces.converter.DoubleConverter.DOUBLE=value must be a number
+javax.faces.converter.DoubleConverter.DOUBLE_detail=value must be a number between 4.9E-324 and 1.7976931348623157E308
+javax.faces.converter.EnumConverter.ENUM=value must be convertible to an enum
+javax.faces.converter.EnumConverter.ENUM_detail=value must be convertible to an enum or from the enum that contains the constant {1}
+javax.faces.converter.EnumConverter.ENUM_NO_CLASS=value must be convertible to an enum or from the enum, but no enum class provided
+javax.faces.converter.EnumConverter.ENUM_NO_CLASS_detail=value must be convertible to an enum or from the enum, but no enum class provided
+javax.faces.converter.FloatConverter.FLOAT=value must be a number
+javax.faces.converter.FloatConverter.FLOAT_detail=value must be a number between 1.4E-45 and 3.4028235E38
+javax.faces.converter.IntegerConverter.INTEGER=value must be an integer
+javax.faces.converter.IntegerConverter.INTEGER_detail=value must be an integer number between -2147483648 and 2147483647
+javax.faces.converter.LongConverter.LONG=value must be an integer
+javax.faces.converter.LongConverter.LONG_detail=must be an integer number between -9223372036854775808 and 9223372036854775807
+javax.faces.converter.NumberConverter.CURRENCY=value must be a currency amount
+javax.faces.converter.NumberConverter.CURRENCY_detail=value must be a currency amount, eg. {1}
+javax.faces.converter.NumberConverter.PERCENT=value must be a percentage amount
+javax.faces.converter.NumberConverter.PERCENT_detail=value must be a percentage amount, eg. {1}
+javax.faces.converter.NumberConverter.NUMBER=value must be a number
+javax.faces.converter.NumberConverter.NUMBER_detail=value must be a number
+javax.faces.converter.NumberConverter.PATTERN=value must be a number
+javax.faces.converter.NumberConverter.PATTERN_detail=value must be a number
+javax.faces.converter.ShortConverter.SHORT=value must be an integer
+javax.faces.converter.ShortConverter.SHORT_detail=value must be an integer number between -32768 and 32767
+
+javax.faces.validator.DoubleRangeValidator.MAXIMUM=value must be less than or equal to {0}
+javax.faces.validator.DoubleRangeValidator.MINIMUM=value must be greater than or equal to {0}
+javax.faces.validator.DoubleRangeValidator.NOT_IN_RANGE=value must be between {0} and {1}
+javax.faces.validator.DoubleRangeValidator.TYPE=value is not of the correct type
+javax.faces.validator.LengthValidator.MAXIMUM=value is must be shorter than or equal to {0} characters
+javax.faces.validator.LengthValidator.MINIMUM=value is must be longer than or equal to {0} characters
+javax.faces.validator.LongRangeValidator.MAXIMUM=value must be less than or equal to {0}
+javax.faces.validator.LongRangeValidator.MINIMUM=value must be greater than or equal to {0}
+javax.faces.validator.LongRangeValidator.NOT_IN_RANGE=value must be between {0} and {1}
+javax.faces.validator.LongRangeValidator.TYPE=value is not of the correct type
+
+javax.faces.validator.NOT_IN_RANGE=value must be between {0} and {1}
+javax.faces.converter.STRING=value could not be converted to a string
+
+blog.feed.deleted=Feed {0} deleted.
+blog.feed.updated=Feed {0} updated.
+blog.feed.added=Feed {0} added.
+blog.feed.accepted=Feed {0} accepted. Its posts will bead read and saved in a moment.
+blog.feed.proposed=Your feed: '{0}' has been added to our propositions queue. It will be now reviewed.
+
+blog.template.deleted=Template {0} of type {1} deleted.
+blog.template.updated=Template {0} of type {1} updated.
+blog.template.added=Template {0} of type {1} added.
+blog.template.new.existingname=A template with that name already exists.
+
+blog.feed.post.invalid=Post: '#0', property: #1, #2.
+blog.feed.enclosure.invalid=Enclosure on post: '#0' with url '#1', property: #2, #3
+blog.feed.image.invalid=Image for post: '#0', property: #1, #2
+
+blog.feed.remote.parseok.nocategories=Parsing the feed was successfull! However entries of this feed do \
+  not contain any category information - please check, if the feed contains only posts, that you'd like \
+  to include. If so, you can proceed.
+blog.feed.remote.parseok.categories=Parsing the feed was successfull! You can now (optionally) choose \
+  a category, from which posts you'd like to include, and proceed.
+blog.feed.remote.parsenotok=Parsing the feed failed, because of the following exception: #0. 
+
+blog.feed.remote.updated=Remote feed {0} updated.
+blog.feed.remote.adding.quickstart=Enter your atom/rss2 feed address in the box to the left; if it is correct \
+  and the feed parses without any problems, you'll be able to proceed and fill in other details of the new feed.
+blog.feed.remote.mod.authors=You can choose how the author of a post is determined: the value can be either taken \
+  as it appears in the feed, can be always overwritten by the blog author (which is configurable later) or can be \
+  replaced by the blog author only when the post author is missing (default).
+
+blog.feed.individual.post.added=Post '{0}' added.
+blog.feed.individual.post.deleted=Post '{0}' deleted.
+
+blog.feed.aggregated.updated=Aggregated feed {0} changed.
+
+blog.feed.new.invalidname=Feed name may only contain small latin letters, numbers and _.
+blog.feed.new.existingname=A feed with that name already exists.
+
+blog.group.invalidname=Group name may only contain small latin letters, numbers and _.
+blog.group.existingname=A group with that name already exists.
+blog.group.deleted=Group '{0}' ({1}) deleted.
+blog.group.updated=Group '{0}' ({1}) updated.
+blog.group.added=Group '{0}' ({1}) added.
+blog.group.cannotdelete=Cannot delete group '{0}' ({1}), as there are still feeds assigned to it.
+
+blog.post.deleted=Post '{0}' deleted.
+
+blog.search.exception=Malformed search query: {0}.
+blog.search.emptyquery=Your query is empty.
+
+blog.configuration.saved=Configuration saved.
+blog.configuration.testmail.sent=Test e-mail sent.
+blog.configuration.testmail.notsent=Exception when sending a test e-mail: {0}.
+
+blog.feed.highlights.adding.info=A highlights feed lets you create a feed out of selected, arbitrary posts. \
+  After adding a new highlights feed, you'll be able to add posts to it by navigating to the post and clicking \
+  a link there. If you'd like to change the order in which the posts appear, or delete some, you'll just need to \
+  edit this feeds 'specific' properties.
+blog.feed.highlights.updated=Highlights feed '{0}' updated.
+blog.feed.highlights.post.added=Post '{0}' added to highlights feed '{1}'.
+
+blog.security.group.admin.added=Group {0} added to administrators.
+blog.security.group.admin.deleted=Group {0} deleted from administrators.
+
+blog.security.group.group.added=Group {0} added to administrators of group {1}.
+blog.security.group.group.deleted=Group {0} deleted from administrators of group {1}.
+
+blog.security.group.feed.added=Group {0} added to administrators of feed {1}.
+blog.security.group.feed.deleted=Group {0} deleted from administrators of feed {1}.
+
+blog.security.user.admin.added=User {0} added to administrators.
+blog.security.user.admin.deleted=User {0} deleted from administrators.
+
+blog.security.user.group.added=User {0} added to administrators of group {1}.
+blog.security.user.group.deleted=User {0} deleted from administrators of group {1}.
+
+blog.security.user.feed.added=User {0} added to administrators of feed {1}.
+blog.security.user.feed.deleted=User {0} deleted from administrators of feed {1}.
+
+blog.security.user.feedview.added=User {0} added to viewers of feed {1}.
+blog.security.user.feedview.deleted=User {0} deleted from viewers of feed {1}.
+
+blog.security.group.feedview.added=Group {0} added to viewers of feed {1}.
+blog.security.group.feedview.deleted=Group {0} deleted from viewers of feed {1}.
\ No newline at end of file

Added: feeds100P26/resources/seam.properties
===================================================================

Added: feeds100P26/resources/templates/atom_standard.vm
===================================================================
--- feeds100P26/resources/templates/atom_standard.vm	                        (rev 0)
+++ feeds100P26/resources/templates/atom_standard.vm	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
+<id>$tools.feedLink($feed, $xmlType)</id>
+<title type="html"><![CDATA[$feed.title]]></title>
+<updated>$tools.formatDate($tools.feedPubDate($feed, $posts))</updated>
+<author>
+   <name>$feed.author</name>
+</author>
+<link rel="alternate" type="text/html" href="$tools.feedPageLink($feed)"/>
+<link rel="self" type="application/atom+xml" href="$tools.feedLink($feed, $xmlType)"/>
+
+#foreach($post in $posts)
+<entry>
+  <id>$tools.postLink($post)</id>
+  <title type="html"><![CDATA[$post.title]]></title>
+  <link rel="alternate" type="text/html" href="$tools.postLink($post)"/>
+
+  <updated>$tools.formatDate($post.modified)</updated>
+  <published>$tools.formatDate($post.published)</published>
+
+  <content type="html" xml:lang="en">
+    <![CDATA[$post.content
+
+    #if($feed.showDelicious or $feed.showDzone or $feed.showDigg)
+    <p>
+    #if($feed.showDzone)
+    <a href="http://www.dzone.com/links/add.html?url=$postToTools.encodeLinkForDelicious($post)&amp;title=$postToTools.encodeTitleForDelicious($post)">
+      Post to DZone</a> &#160;
+    #end
+    #if($feed.showDelicious)
+    <a href="http://del.icio.us/post?v=4&amp;url=$postToTools.encodeLinkForDelicious($post)&amp;title=$postToTools.encodeTitleForDelicious($post)">
+      Post to del.icio.us</a> &#160;
+    #end
+    #if($feed.showDigg)
+    <a href="http://digg.com/submit?url=$postToTools.encodeLinkForDigg($post)&amp;title=$postToTools.encodeTitleForDigg($post)&amp;bodytext=$postToTools.encodeBodyForDigg($post)&amp;media=news&amp;topic=programming">
+      Digg this!</a> &#160;
+    #end
+    </p>
+    #end
+    ]]>
+  </content>
+
+  <author>
+    <name>$post.effectiveAuthor</name>
+  </author>
+
+  #foreach($enclosure in $post.enclosures)
+  <link href="$enclosure.url" rel="enclosure" length="$enclosure.length" type="$enclosure.type" />
+  #end
+
+  #foreach($image in $post.images)
+  <itunes:image href="$image.url" />
+  #end
+</entry>
+#end
+</feed>

Added: feeds100P26/resources/templates/rss1_standard.vm
===================================================================
--- feeds100P26/resources/templates/rss1_standard.vm	                        (rev 0)
+++ feeds100P26/resources/templates/rss1_standard.vm	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+
+<rdf:RDF
+  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+  xmlns="http://purl.org/rss/1.0/"
+>
+        <channel>
+                <title>$feed.title</title>
+                <link>$tools.feedPageLink($feed)</link>
+
+                <items>
+                    <rdf:Seq>
+                    #foreach($post in $posts)
+                        <rdf:li resource="$tools.postLink($post)" />
+                    #end
+                    </rdf:Seq>
+                </items>
+        </channel>
+
+        #foreach($post in $posts)
+        <item rdf:about="$tools.postLink($post)">
+            <title><![CDATA[$post.title]]></title>
+            <link>$tools.postLink($post)</link>
+            <description><![CDATA[$post.content]]></description>
+        </item>
+        #end
+</rdf:RDF>
\ No newline at end of file

Added: feeds100P26/resources/templates/rss2_standard.vm
===================================================================
--- feeds100P26/resources/templates/rss2_standard.vm	                        (rev 0)
+++ feeds100P26/resources/templates/rss2_standard.vm	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0"
+	xmlns:content="http://purl.org/rss/1.0/modules/content/"
+	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
+	xmlns:dc="http://purl.org/dc/elements/1.1/">
+        <channel>
+                <title>$feed.title</title>
+                <link>$tools.feedPageLink($feed)</link>
+                <language>en</language>
+                <docs>http://blogs.law.harvard.edu/tech/rss</docs>
+                <generator>JBoss Feeds</generator>
+                <managingEditor>$feed.author</managingEditor>
+                <pubDate>$tools.formatDate($tools.feedPubDate($feed, $posts))</pubDate>
+
+                #foreach($post in $posts)
+                <item>
+                        <title><![CDATA[$post.title]]></title>
+                        <link>$tools.postLink($post)</link>
+                        <description><![CDATA[$post.content]]></description>
+                        <guid>$tools.postLink($post)</guid>
+                        <pubDate>$tools.formatDate($post.published)</pubDate>
+                        <dc:creator>$post.effectiveAuthor</dc:creator>
+                </item>
+                #end
+
+        </channel>
+</rss>
\ No newline at end of file

Added: feeds100P26/resources/velocity.properties
===================================================================
--- feeds100P26/resources/velocity.properties	                        (rev 0)
+++ feeds100P26/resources/velocity.properties	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,9 @@
+input.encoding = UTF-8
+output.encoding = UTF-8
+
+resource.loader = database
+
+database.resource.loader.description = Velocity File Resource Loader
+database.resource.loader.class = org.jboss.blog.session.xml.velocity.DatabaseResourceLoader
+database.resource.loader.cache = false
+database.resource.loader.modificationCheckInterval = 2
\ No newline at end of file

Added: feeds100P26/resources-portlet/WEB-INF/blog-object.xml
===================================================================
--- feeds100P26/resources-portlet/WEB-INF/blog-object.xml	                        (rev 0)
+++ feeds100P26/resources-portlet/WEB-INF/blog-object.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE deployments PUBLIC
+        "-//JBoss Portal//DTD Portal Object 2.6//EN"
+        "http://www.jboss.org/portal/dtd/portal-object_2_6.dtd">
+<deployments>
+    <deployment>
+        <parent-ref>default.default</parent-ref>
+        <if-exists>overwrite</if-exists>
+        <window>
+            <window-name>DefaultBlogPortletWindow</window-name>
+            <instance-ref>
+                DefaultBlogPortletInstance
+            </instance-ref>
+            <region>right</region>
+            <height>3</height>
+        </window>
+    </deployment>
+    <deployment>
+        <parent-ref>default.communityhome</parent-ref>
+        <if-exists>overwrite</if-exists>
+        <window>
+            <window-name>CommunityBlogPortletWindow</window-name>
+            <instance-ref>
+                CommunityBlogPortletInstance
+            </instance-ref>
+            <region>right1_6</region>
+            <height>1</height>
+        </window>
+    </deployment>
+</deployments>

Added: feeds100P26/resources-portlet/WEB-INF/jboss-app.xml
===================================================================
--- feeds100P26/resources-portlet/WEB-INF/jboss-app.xml	                        (rev 0)
+++ feeds100P26/resources-portlet/WEB-INF/jboss-app.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,3 @@
+<jboss-app>
+    <app-name>feeds</app-name>
+</jboss-app>
\ No newline at end of file

Added: feeds100P26/resources-portlet/WEB-INF/jboss-portlet.xml
===================================================================
--- feeds100P26/resources-portlet/WEB-INF/jboss-portlet.xml	                        (rev 0)
+++ feeds100P26/resources-portlet/WEB-INF/jboss-portlet.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<!DOCTYPE portlet-app PUBLIC
+        "-//JBoss Portal//DTD JBoss Portlet 2.6//EN"
+        "http://www.jboss.org/portal/dtd/jboss-portlet_2_6.dtd">
+<portlet-app>
+    <portlet>
+        <portlet-name>BlogPortlet</portlet-name>
+    </portlet>
+</portlet-app>
\ No newline at end of file

Added: feeds100P26/resources-portlet/WEB-INF/portlet-instances.xml
===================================================================
--- feeds100P26/resources-portlet/WEB-INF/portlet-instances.xml	                        (rev 0)
+++ feeds100P26/resources-portlet/WEB-INF/portlet-instances.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE deployments PUBLIC
+        "-//JBoss Portal//DTD Portlet Instances 2.6//EN"
+        "http://www.jboss.org/portal/dtd/portlet-instances_2_6.dtd">
+<deployments>
+    <deployment>
+        <if-exists>overwrite</if-exists>
+        <instance>
+            <instance-id>DefaultBlogPortletInstance</instance-id>
+            <portlet-ref>BlogPortlet</portlet-ref>
+            <preferences>
+                <preference>
+                    <name>feedName</name>
+                    <value>all</value>
+                </preference>
+                <preference>
+                    <name>numberOfPosts</name>
+                    <value>3</value>
+                </preference>
+                <preference>
+                    <name>summaryLength</name>
+                    <value>0</value>
+                </preference>
+                <preference>
+                    <name>showDate</name>
+                    <value>true</value>
+                </preference>
+                <preference>
+                    <name>jsp</name>
+                    <value>/view_main.jsp</value>
+                </preference>
+            </preferences>
+        </instance>
+    </deployment>
+    <deployment>
+        <if-exists>overwrite</if-exists>
+        <instance>
+            <instance-id>CommunityBlogPortletInstance</instance-id>
+            <portlet-ref>BlogPortlet</portlet-ref>
+            <preferences>
+                <preference>
+                    <name>feedName</name>
+                    <value>communityhome</value>
+                </preference>
+                <preference>
+                    <name>numberOfPosts</name>
+                    <value>10</value>
+                </preference>
+                <preference>
+                    <name>summaryLength</name>
+                    <value>0</value>
+                </preference>
+                <preference>
+                    <name>showDate</name>
+                    <value>false</value>
+                </preference>
+                <preference>
+                    <name>jsp</name>
+                    <value>/view.jsp</value>
+                </preference>
+            </preferences>
+        </instance>
+    </deployment>
+</deployments>

Added: feeds100P26/resources-portlet/WEB-INF/portlet.xml
===================================================================
--- feeds100P26/resources-portlet/WEB-INF/portlet.xml	                        (rev 0)
+++ feeds100P26/resources-portlet/WEB-INF/portlet.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
+                                 http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
+             version="1.0">
+    <portlet>
+        <portlet-name>BlogPortlet</portlet-name>
+        <portlet-class>org.jboss.blog.portlet.BlogPortlet</portlet-class>
+        <supports>
+            <mime-type>text/html</mime-type>
+            <portlet-mode>VIEW</portlet-mode>
+        </supports>
+        <supported-locale>en</supported-locale>
+        <expiration-cache>60</expiration-cache>
+        <portlet-info>
+            <title>JBoss Blog Portlet</title>
+        </portlet-info>
+        <portlet-preferences>
+            <preference>
+                <name>feedName</name>
+            </preference>
+            <preference>
+                <name>numberOfPosts</name>
+                <value>5</value>
+            </preference>
+            <preference>
+                <name>summaryLength</name>
+                <value>200</value>
+            </preference>
+            <preference>
+                <name>showDate</name>
+                <value>true</value>
+            </preference>
+            <preference>
+                <name>jsp</name>
+                <value>/view.jsp</value>
+            </preference>
+        </portlet-preferences>
+    </portlet>
+</portlet-app>
+

Added: feeds100P26/resources-portlet/WEB-INF/web.xml
===================================================================
--- feeds100P26/resources-portlet/WEB-INF/web.xml	                        (rev 0)
+++ feeds100P26/resources-portlet/WEB-INF/web.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         version="2.5">
+    <filter>
+        <filter-name>Resources Filter</filter-name>
+        <filter-class>org.jboss.shotoku.web.ShotokuResourcesFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>Resources Filter</filter-name>
+        <url-pattern>/*</url-pattern>
+        <dispatcher>ERROR</dispatcher>
+        <dispatcher>FORWARD</dispatcher>
+        <dispatcher>INCLUDE</dispatcher>
+        <dispatcher>REQUEST</dispatcher>
+    </filter-mapping>
+</web-app>

Added: feeds100P26/src/action/org/jboss/blog/servlet/FeedsServlet.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/servlet/FeedsServlet.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/servlet/FeedsServlet.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,80 @@
+package org.jboss.blog.servlet;
+
+import org.jboss.blog.service.FeedNotFoundException;
+import org.jboss.blog.session.xml.velocity.InvalidTemplateTypeException;
+import org.jboss.blog.session.xml.XmlService;
+import org.jboss.blog.session.xml.content.ContentResponse;
+import org.jboss.blog.session.xml.content.ServletResponseContentResponse;
+import org.jboss.seam.Component;
+import org.jboss.seam.log.Logging;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FeedsServlet extends HttpServlet {
+    private void writeErrorMessage(String message, HttpServletResponse response) throws IOException {
+        response.setContentType("text/html");
+        response.getWriter().println(message);
+        response.getWriter().flush();
+    }
+
+    private void rollbackTx(UserTransaction tx, boolean txStarted) {
+        try {
+            if (txStarted) {
+                tx.rollback();
+            }
+        } catch (SystemException e1) {
+            Logging.getLog(FeedsServlet.class).error("Exception when rolling back the transaction", e1);
+        }
+    }
+
+    protected void service(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+        String feedType = request.getParameter("type");
+        String feedName = request.getParameter("name");
+        String feedKey = request.getParameter("key");
+
+        UserTransaction tx = null;
+        boolean txStarted = false;
+        try {
+            tx = (UserTransaction) Component.getInstance("org.jboss.seam.transaction.transaction");
+            if (tx.getStatus() != Status.STATUS_ACTIVE) {
+                txStarted = true;
+                tx.begin();
+            }
+
+            // TODO: expires, created, if-not-modified-since
+
+            ContentResponse contentResponse = new ServletResponseContentResponse(response);
+
+            XmlService xmlService = (XmlService) Component.getInstance("xmlService");
+            xmlService.writeXml(feedType, feedName, feedKey, contentResponse);
+            response.getWriter().flush();
+
+            if (txStarted) {
+                tx.commit();
+            }
+        } catch (InvalidTemplateTypeException e) {
+            rollbackTx(tx, txStarted);
+
+            writeErrorMessage("The given feed type does not exist.", response);
+        } catch (FeedNotFoundException e) {
+            rollbackTx(tx, txStarted);
+
+            writeErrorMessage("Requested feed not found.", response);
+        } catch (Exception e) {
+            rollbackTx(tx, txStarted);
+
+            throw new ServletException(e);
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/cache/CacheManager.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/cache/CacheManager.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/cache/CacheManager.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,22 @@
+package org.jboss.blog.session.cache;
+
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.session.xml.content.InMemoryContentResponse;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface CacheManager {
+    void evictAll();
+
+    void putFeedPosts(RestrictedFeed feed, List<? extends RestrictedPost> posts, int from, int to);
+
+    List<? extends RestrictedPost> getFeedPosts(String feedName, int from, int to);
+
+    void putFeedXml(String feedName, String feedType, InMemoryContentResponse xml);
+
+    InMemoryContentResponse getFeedXml(String feedName, String feedType);
+}

Added: feeds100P26/src/action/org/jboss/blog/session/cache/CacheManagerHashMapImpl.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/cache/CacheManagerHashMapImpl.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/cache/CacheManagerHashMapImpl.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,131 @@
+package org.jboss.blog.session.cache;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Logger;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.RestrictedImage;
+import org.jboss.blog.model.RestrictedCategory;
+import org.jboss.blog.model.RestrictedEnclosure;
+import org.jboss.blog.session.xml.content.InMemoryContentResponse;
+
+import javax.annotation.PostConstruct;
+import java.lang.ref.SoftReference;
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * TODO: check Soft References
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("cacheManager")
+ at Scope(ScopeType.APPLICATION)
+ at AutoCreate
+public class CacheManagerHashMapImpl implements CacheManager {
+    @Logger
+    private Log log;
+
+    private Map<Object, Object> cache;
+
+    @PostConstruct
+    public void init() {
+        cache = new ConcurrentHashMap<Object, Object>();
+    }
+
+    //
+
+    private <T> T getFromSoftReference(SoftReference<T> reference) {
+        if (reference == null) {
+            return null;
+        }
+
+        return reference.get();
+    }
+
+    private void readPost(RestrictedPost post) {
+        post.getFeed().getId();
+
+        for (RestrictedCategory category : post.getCategories()) {
+            category.getId();
+        }
+
+        for (RestrictedEnclosure enclosure : post.getEnclosures()) {
+            enclosure.getId();
+        }
+
+        for (RestrictedImage image : post.getImages()) {
+            image.getId();
+        }
+    }
+
+    private void readPosts(List<? extends RestrictedPost> posts) {
+        for (RestrictedPost post : posts) {
+            readPost(post);
+        }
+    }
+
+    //
+
+    public void evictAll() {
+        log.debug("Evicting everything from the cache.");
+        cache.clear();
+    }
+
+    //
+
+    private String getFeedPostsFqn(String feedName, int from, int to) {
+        return feedName + "/" + from + "/" + to;
+    }
+
+    private <T> List<T> copyList(List<T> original) {
+        return new ArrayList<T>(original);
+    }
+
+    public void putFeedPosts(RestrictedFeed feed, List<? extends RestrictedPost> posts, int from, int to) {
+        readPosts(posts);
+
+        log.debug("Putting feed '#0' posts into the cache, from #1 to #2.", feed.getName(), from, to);
+        
+        cache.put(getFeedPostsFqn(feed.getName(), from, to), new SoftReference<List<? extends RestrictedPost>>(
+                copyList(posts)));
+    }
+
+    public List<? extends RestrictedPost> getFeedPosts(String feedName, int from, int to) {
+        //noinspection unchecked
+        List<? extends RestrictedPost> inCache =
+                getFromSoftReference(((SoftReference<List<? extends RestrictedPost>>) cache.get(
+                        getFeedPostsFqn(feedName, from, to))));
+        log.debug("Getting feed '#0' posts from the cache, from #1 to #2, result: #3.",
+                feedName, from, to, inCache);
+        return inCache == null ? null : copyList(inCache);
+    }
+
+    //
+
+    private final static String FEED_XML = "$feedxml";
+
+    private String getFeedXmlFqn(String feedName, String feedType) {
+        return FEED_XML + "/" + feedName + "/" + (feedType == null ? null : feedType.toLowerCase());
+    }
+
+    public void putFeedXml(String feedName, String feedType, InMemoryContentResponse xml) {
+        log.debug("Putting xml for feed '#0' to the cache, type: #1.", feedName, feedType);
+        cache.put(getFeedXmlFqn(feedName, feedType),
+                new SoftReference<InMemoryContentResponse>(xml));
+    }
+
+    public InMemoryContentResponse getFeedXml(String feedName, String feedType) {
+        //noinspection unchecked
+        InMemoryContentResponse inCache = getFromSoftReference(((SoftReference<InMemoryContentResponse>)
+                cache.get(getFeedXmlFqn(feedName, feedType))));
+        log.debug("Getting xml for feed '#0' from the cache, type: #1, result: #2.",
+                feedName, feedType, inCache);
+        return inCache;
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/cache/FeedsChangesObserver.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/cache/FeedsChangesObserver.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/cache/FeedsChangesObserver.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,47 @@
+package org.jboss.blog.session.cache;
+
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.In;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Template;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("feedsChangesObserver")
+public class FeedsChangesObserver {
+    @In
+    private CacheManager cacheManager;
+
+    @Observer({"org.jboss.blog.feed.updated", "org.jboss.blog.feed.deleted"})
+    public void feedUpdated(Feed feed) {
+        cacheManager.evictAll();
+    }
+
+    @Observer({"org.jboss.blog.feed.added"})
+    public void feedAdded(Feed feed) {
+        cacheManager.evictAll();
+    }
+
+    @Observer({"org.jboss.blog.post.updated", "org.jboss.blog.post.deleted"})
+    public void postUpdated(Post post) {
+        cacheManager.evictAll();
+    }
+
+    @Observer("org.jboss.blog.post.added")
+    public void postAdded(Post post) {
+        cacheManager.evictAll();
+    }
+
+    @Observer({"org.jboss.blog.template.updated", "org.jboss.blog.template.deleted"})
+    public void templateUpdated(Template template) {
+        cacheManager.evictAll();
+    }
+
+    @Observer("org.jboss.blog.template.added")
+    public void templateAdded(Template template) {
+        cacheManager.evictAll();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/category/CategoryServiceBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/category/CategoryServiceBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/category/CategoryServiceBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,35 @@
+package org.jboss.blog.session.category;
+
+import org.jboss.blog.model.Category;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.STATELESS)
+ at Name("categoryService")
+ at AutoCreate
+public class CategoryServiceBean {
+    @In
+    private EntityManager entityManager;
+
+    public Category getCategory(String name) {
+        try {
+            return (Category) entityManager.createQuery("select cat from Category cat where cat.name = ?1")
+                    .setParameter(1, name).getSingleResult();
+        } catch (NoResultException e) {
+            Category ret = new Category(name);
+            entityManager.persist(ret);
+            entityManager.flush();
+
+            return ret;
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/configuration/ConfigurationManager.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/configuration/ConfigurationManager.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/configuration/ConfigurationManager.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,76 @@
+package org.jboss.blog.session.configuration;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.faces.Renderer;
+import org.jboss.blog.model.configuration.Configuration;
+import org.jboss.blog.tools.GeneralTools;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.faces.application.FacesMessage;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("configurationManager")
+ at Scope(ScopeType.APPLICATION)
+ at AutoCreate
+public class ConfigurationManager {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In(create = true)
+    private Renderer renderer;
+
+    private Configuration initNewConfiguration() {
+        Configuration conf = new Configuration();
+        conf.setConnectionTimeout(10000);
+        conf.setReadTimeout(6000);
+        conf.setUpdateInterval(900);
+
+        return conf;
+    }
+
+    public synchronized Configuration getConfiguration() {
+        try {
+            return (Configuration) entityManager.createQuery("select conf from Configuration conf").getSingleResult();
+        } catch (NoResultException e) {
+            Configuration conf = initNewConfiguration();
+            entityManager.persist(conf);
+            entityManager.flush();
+
+            return conf;
+        } catch (NonUniqueResultException e) {
+            throw new RuntimeException("There should be at most 1 configuration entity!", e);
+        }
+    }
+
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void save() {
+        entityManager.flush();
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.configuration.saved");
+    }
+
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void testEmail() {
+        save();
+
+        try {
+            renderer.render("/emails/test_email.xhtml");
+            facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.configuration.testmail.sent");
+        } catch (Exception e) {
+            facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_ERROR, "blog.configuration.testmail.notsent",
+                    GeneralTools.getExceptionStackTrace(e));
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/converter/FeedConverter.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/converter/FeedConverter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/converter/FeedConverter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,37 @@
+package org.jboss.blog.session.converter;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.FeedNotFoundException;
+import org.jboss.blog.session.exceptions.FeedNotFoundRuntimeException;
+import org.jboss.seam.Component;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.faces.Converter;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("feedConverter")
+ at BypassInterceptors
+ at Converter
+public class FeedConverter implements javax.faces.convert.Converter {
+    @Transactional
+    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
+        FeedsService feedsService = (FeedsService) Component.getInstance("feedsService");
+
+        try {
+            return feedsService.getFeed(value);
+        } catch (FeedNotFoundException e) {
+            throw new FeedNotFoundRuntimeException(e.getMessage());
+        }
+    }
+
+    public String getAsString(FacesContext context, UIComponent cmp, Object value) {
+        return value == null ? null : ((Feed) value).getName();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/converter/GroupConverter.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/converter/GroupConverter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/converter/GroupConverter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,41 @@
+package org.jboss.blog.session.converter;
+
+import org.jboss.blog.model.Group;
+import org.jboss.seam.Component;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.faces.Converter;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("groupConverter")
+ at BypassInterceptors
+ at Converter
+public class GroupConverter implements javax.faces.convert.Converter {
+    @Transactional
+    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
+        EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
+
+        entityManager.joinTransaction();
+
+        Integer id;
+        id = Integer.parseInt(value);
+
+        return entityManager.find(Group.class, id);
+    }
+
+    public String getAsString(FacesContext context, UIComponent cmp, Object value) {
+        if (value == null) {
+            return null;
+        }
+
+        Integer id = ((Group) value).getId();
+        return id == null ? null : id.toString();
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/converter/PostConverter.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/converter/PostConverter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/converter/PostConverter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,38 @@
+package org.jboss.blog.session.converter;
+
+import org.jboss.blog.model.Post;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.PostNotFoundException;
+import org.jboss.blog.session.exceptions.PostNotFoundRuntimeException;
+import org.jboss.seam.Component;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.faces.Converter;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("postConverter")
+ at BypassInterceptors
+ at Converter
+public class PostConverter implements javax.faces.convert.Converter {
+    @Transactional
+    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
+        FeedsService feedsService = (FeedsService) Component.getInstance("feedsService");
+
+        try {
+            return feedsService.getPost(value);
+        } catch (PostNotFoundException e) {
+            throw new PostNotFoundRuntimeException(e.getMessage());
+        }
+    }
+
+    public String getAsString(FacesContext context, UIComponent cmp, Object value) {
+        return value == null ? null : ((Post) value).getTitleAsId();
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/converter/TemplateConverter.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/converter/TemplateConverter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/converter/TemplateConverter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,41 @@
+package org.jboss.blog.session.converter;
+
+import org.jboss.blog.model.Template;
+import org.jboss.seam.Component;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.faces.Converter;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("templateConverter")
+ at BypassInterceptors
+ at Converter
+public class TemplateConverter implements javax.faces.convert.Converter {
+    @Transactional
+    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
+        EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
+
+        entityManager.joinTransaction();
+
+        Integer id;
+        id = Integer.parseInt(value);
+
+        return entityManager.find(Template.class, id);
+    }
+
+    public String getAsString(FacesContext context, UIComponent cmp, Object value) {
+        if (value == null) {
+            return null;
+        }
+
+        Integer id = ((Template) value).getId();
+        return id == null ? null : id.toString();
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/exceptions/FeedNotFoundRuntimeException.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/exceptions/FeedNotFoundRuntimeException.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/exceptions/FeedNotFoundRuntimeException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.session.exceptions;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FeedNotFoundRuntimeException extends RuntimeException {
+    public FeedNotFoundRuntimeException() {
+    }
+
+    public FeedNotFoundRuntimeException(String message) {
+        super(message);
+    }
+
+    public FeedNotFoundRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public FeedNotFoundRuntimeException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/exceptions/PostNotFoundRuntimeException.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/exceptions/PostNotFoundRuntimeException.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/exceptions/PostNotFoundRuntimeException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.session.exceptions;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PostNotFoundRuntimeException extends RuntimeException {
+    public PostNotFoundRuntimeException() {
+    }
+
+    public PostNotFoundRuntimeException(String message) {
+        super(message);
+    }
+
+    public PostNotFoundRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public PostNotFoundRuntimeException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/FeedsServiceImpl.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/FeedsServiceImpl.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/FeedsServiceImpl.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,140 @@
+package org.jboss.blog.session.feed;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.service.FeedNotFoundException;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.PostNotFoundException;
+import org.jboss.blog.session.feed.type.FeedTypes;
+import org.jboss.blog.session.feed.posts.DatabaseFeedPosts;
+import org.jboss.blog.session.cache.CacheManager;
+import org.jboss.blog.session.security.tools.FeedSecurityTools;
+import org.jboss.blog.session.security.FeedsIdentity;
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.ScopeType;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("feedsService")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class FeedsServiceImpl implements FeedsService {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FeedTypes feedTypes;
+
+    @In
+    private DatabaseFeedPosts databaseFeedPosts;
+
+    @In
+    private CacheManager cacheManager;
+
+    @In
+    private FeedsIdentity identity;
+
+    @Logger
+    private Log log;
+
+    public Post getPost(String titleAsId) throws PostNotFoundException {
+        log.debug("Reading post '#0' from the DB.", titleAsId);
+
+        if (titleAsId == null) {
+            throw new PostNotFoundException(titleAsId);
+        }
+
+        try {
+            return (Post) entityManager.createQuery("select post from Post post where post.titleAsId = ?1")
+                    .setParameter(1, titleAsId).setHint("org.hibernate.cacheable", Boolean.TRUE).getSingleResult();
+        } catch (NoResultException e) {
+            throw new PostNotFoundException(titleAsId);
+        }
+    }
+
+    public Feed getFeed(String feedName) throws FeedNotFoundException {
+        log.debug("Reading feed '#0' from the DB.", feedName);
+
+        if (feedName == null) {
+            throw new FeedNotFoundException(feedName);
+        }
+
+        try {
+            return (Feed) entityManager.createQuery("select feed from Feed feed where feed.name = ?1")
+                    .setParameter(1, feedName).setHint("org.hibernate.cacheable", Boolean.TRUE).getSingleResult();
+        } catch (NoResultException e) {
+            throw new FeedNotFoundException(feedName);
+        }
+    }
+
+    public List<? extends RestrictedPost> getPosts(RestrictedFeed feed, int from, int to) {
+        boolean restricted = false;
+
+        // We want to get restricted feeds, if we are logged in, the current feed is restricted and
+        // we have sufficient permissions to view the feed.
+        if (identity.isLoggedIn() && feed.isFeedRestricted() && FeedSecurityTools.canViewFeed(feed, true)) {
+            restricted = true;
+        }
+
+        return getPosts(feed, from, to, restricted);
+    }
+
+    public List<? extends RestrictedPost> getPosts(RestrictedFeed feed, int from, int to, boolean restricted) {
+        boolean isFeedRestricted = feed.isFeedRestricted();
+
+        // If the feed isn't restricted, then we set the flag not to include restricted posts.
+        if (!isFeedRestricted) {
+            restricted = false;
+        }
+
+        // We return an empty list if:
+        // - we don't want restricted posts, but this feed is restricted
+        // - we want restricted posts, this feed is restricted, but we can't view it.
+        if ((!restricted && isFeedRestricted) || (restricted && isFeedRestricted &&
+                        !FeedSecurityTools.canViewFeed(feed, restricted))) {
+            log.debug("Reading restricted feed '#0', returning an empty list.", feed.getName());
+            return new ArrayList<RestrictedPost>();
+        }
+
+        // We use the cache only if the feed isn't restricted.
+        if (!isFeedRestricted) {
+            List<? extends RestrictedPost> inCache = cacheManager.getFeedPosts(feed.getName(), from, to);
+
+            if (inCache != null) {
+                return inCache;
+            }
+        }
+
+        List<? extends RestrictedPost> posts;
+
+        try {
+            log.debug("Reading feed '#0' posts from the DB, from #1 to #2, restricted = #3.", feed.getName(), from, to,
+                    restricted);
+            posts = feedTypes.getFeedDao(feed).getPosts(from, to, restricted);
+        } catch (InvalidFeedTypeException e) {
+            log.error(e);
+            return new ArrayList<RestrictedPost>();
+        }
+
+        if (posts != null && !isFeedRestricted) {
+            cacheManager.putFeedPosts(feed, posts, from, to);
+        }
+
+        return posts;
+    }
+
+    public List<? extends RestrictedPost> getPosts(int from, int to) {
+        log.debug("Reading posts from the DB, from #0 to #1.", from, to);
+
+        return databaseFeedPosts.getPosts(from, to);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/InvalidFeedTypeException.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/InvalidFeedTypeException.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/InvalidFeedTypeException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.session.feed;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class InvalidFeedTypeException extends Exception {
+    public InvalidFeedTypeException() {
+    }
+
+    public InvalidFeedTypeException(String message) {
+        super(message);
+    }
+
+    public InvalidFeedTypeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public InvalidFeedTypeException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/dao/AggregatedFeedDao.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/dao/AggregatedFeedDao.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/dao/AggregatedFeedDao.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,34 @@
+package org.jboss.blog.session.feed.dao;
+
+import org.jboss.blog.model.feed.AggregatedFeed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.session.feed.posts.AggregatedFeedPosts;
+import org.jboss.blog.session.feed.type.FeedType;
+import org.jboss.seam.Component;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at FeedType(
+        name = "aggregated",
+        addPage = "/manage/aggregated/aggregated_add.xhtml",
+        editPage = "/manage/aggregated/aggregated_edit.xhtml",
+        model = AggregatedFeed.class)
+public class AggregatedFeedDao implements FeedDao {
+    private AggregatedFeed aggregatedFeed;
+
+    public AggregatedFeedDao(AggregatedFeed aggregatedFeed) {
+        this.aggregatedFeed = aggregatedFeed;
+    }
+
+    public List<? extends RestrictedPost> getPosts(int from, int to, boolean restricted) {
+        return ((AggregatedFeedPosts) Component.getInstance("aggregatedFeedPosts")).getPosts(
+                aggregatedFeed, from, to, restricted);
+    }
+
+    public void update() {
+
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/dao/FeedDao.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/dao/FeedDao.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/dao/FeedDao.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,15 @@
+package org.jboss.blog.session.feed.dao;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.session.update.UpdateException;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface FeedDao {
+    public List<? extends RestrictedPost> getPosts(int from, int to, boolean restricted);
+
+    public void update() throws UpdateException;
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/dao/HighlightsFeedDao.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/dao/HighlightsFeedDao.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/dao/HighlightsFeedDao.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,45 @@
+package org.jboss.blog.session.feed.dao;
+
+import org.jboss.blog.model.feed.HighlightsFeed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.session.feed.type.FeedType;
+import org.jboss.blog.session.update.UpdateException;
+import org.jboss.blog.session.security.tools.FeedSecurityTools;
+import org.jboss.blog.tools.GeneralTools;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at FeedType(
+        name = "highlights",
+        addPage = "/manage/highlights/highlights_add.xhtml",
+        editPage = "/manage/highlights/highlights_edit.xhtml",
+        model = HighlightsFeed.class)
+public class HighlightsFeedDao implements FeedDao {
+    private HighlightsFeed highlightsFeed;
+
+    public HighlightsFeedDao(HighlightsFeed highlightsFeed) {
+        this.highlightsFeed = highlightsFeed;
+    }
+
+    public List<? extends RestrictedPost> getPosts(int from, int to, boolean restricted) {
+        List<Post> posts = GeneralTools.subList(highlightsFeed.getSelectedPosts(), from, to);
+        List<Post> ret = new ArrayList<Post>();
+
+        for (Post post : posts) {
+            if (FeedSecurityTools.canViewFeed(post.getFeed(), restricted)) {
+                ret.add(post);
+            }
+        }
+
+        return ret;
+    }
+
+    public void update() throws UpdateException {
+
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/feed/dao/IndividualPostsFeedDao.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/dao/IndividualPostsFeedDao.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/dao/IndividualPostsFeedDao.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,36 @@
+package org.jboss.blog.session.feed.dao;
+
+import org.jboss.blog.model.feed.IndividualPostsFeed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.session.feed.posts.DatabaseFeedPosts;
+import org.jboss.blog.session.feed.type.FeedType;
+import org.jboss.blog.session.feed.update.IndividualPostsFeedUpdate;
+import org.jboss.blog.session.update.UpdateException;
+import org.jboss.seam.Component;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at FeedType(
+        name = "individual posts",
+        addPage = "/manage/individual/individual_add.xhtml",
+        editPage = "/manage/individual/individual_edit.xhtml",
+        model = IndividualPostsFeed.class)
+public class IndividualPostsFeedDao implements FeedDao {
+    private IndividualPostsFeed individualPostsFeed;
+
+    public IndividualPostsFeedDao(IndividualPostsFeed individualPostsFeed) {
+        this.individualPostsFeed = individualPostsFeed;
+    }
+
+    public List<? extends RestrictedPost> getPosts(int from, int to, boolean restricted) {
+        return ((DatabaseFeedPosts) Component.getInstance("databaseFeedPosts")).getPosts(
+                individualPostsFeed, from, to);
+    }
+
+    public void update() throws UpdateException {
+        ((IndividualPostsFeedUpdate) Component.getInstance("individualPostsFeedUpdate")).update(individualPostsFeed);
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/feed/dao/RemoteFeedDao.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/dao/RemoteFeedDao.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/dao/RemoteFeedDao.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,36 @@
+package org.jboss.blog.session.feed.dao;
+
+import org.jboss.blog.model.feed.RemoteFeed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.session.feed.posts.DatabaseFeedPosts;
+import org.jboss.blog.session.feed.type.FeedType;
+import org.jboss.blog.session.feed.update.RemoteFeedUpdate;
+import org.jboss.blog.session.update.UpdateException;
+import org.jboss.seam.Component;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at FeedType(
+        name = "remote",
+        addPage = "/manage/remote/remote_add.xhtml",
+        editPage = "/manage/remote/remote_edit.xhtml",
+        model = RemoteFeed.class)
+public class RemoteFeedDao implements FeedDao {
+    private RemoteFeed remoteFeed;
+
+    public RemoteFeedDao(RemoteFeed remoteFeed) {
+        this.remoteFeed = remoteFeed;
+    }
+
+    public List<? extends RestrictedPost> getPosts(int from, int to, boolean restricted) {
+        return ((DatabaseFeedPosts) Component.getInstance("databaseFeedPosts")).getPosts(
+                remoteFeed, from, to);
+    }
+
+    public void update() throws UpdateException {
+        ((RemoteFeedUpdate) Component.getInstance("remoteFeedUpdate")).update(remoteFeed);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/lock/FeedsLocksBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/lock/FeedsLocksBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/lock/FeedsLocksBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,37 @@
+package org.jboss.blog.session.feed.lock;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+
+import javax.annotation.PostConstruct;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("feedsLocks")
+ at Scope(ScopeType.APPLICATION)
+ at AutoCreate
+public class FeedsLocksBean {
+    private Map<String, Lock> locks;
+
+    @PostConstruct
+    public void init() {
+        locks = new ConcurrentHashMap<String, Lock>();
+    }
+
+    public Lock getLockForFeed(String feedName) {
+        synchronized(this) {
+            if (!locks.containsKey(feedName)) {
+                locks.put(feedName, new ReentrantLock());
+            }
+        }
+
+        return locks.get(feedName);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedDeleteListener.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedDeleteListener.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedDeleteListener.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,57 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.AggregatedFeed;
+import org.jboss.blog.service.GroupsService;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("aggregatedDeleteListener")
+ at Scope(ScopeType.STATELESS)
+public class AggregatedDeleteListener {
+    @In
+    private GroupsService groupsService;
+
+    private void deleteFrom(Feed feedToDelete, List<Feed> feeds) {
+        for (Feed feed : feeds) {
+            if (feed instanceof AggregatedFeed) {
+                AggregatedFeed aggregatedFeed = (AggregatedFeed) feed;
+                aggregatedFeed.getFeeds().remove(feedToDelete);
+            }
+        }
+    }
+
+    private void deleteFrom(Group groupToDelete, List<Feed> feeds) {
+        for (Feed feed : feeds) {
+            if (feed instanceof AggregatedFeed) {
+                AggregatedFeed aggregatedFeed = (AggregatedFeed) feed;
+                aggregatedFeed.getGroups().remove(groupToDelete);
+            }
+        }
+    }
+
+    @Observer("org.jboss.blog.feed.before_delete")
+    public void beforeFeedDeleted(Feed feedToDelete) {
+        for (Group group : groupsService.getAllGroups()) {
+            deleteFrom(feedToDelete, groupsService.acceptedFeeds(group));
+            deleteFrom(feedToDelete, groupsService.unacceptedFeeds(group));
+        }
+    }
+
+    @Observer("org.jboss.blog.group.before_delete")
+    public void beforeGroupDeleted(Group groupToDelete) {
+        for (Group group : groupsService.getAllGroups()) {
+            deleteFrom(groupToDelete, groupsService.acceptedFeeds(group));
+            deleteFrom(groupToDelete, groupsService.unacceptedFeeds(group));
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedFeedModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedFeedModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/AggregatedFeedModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,227 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.blog.model.feed.AggregatedFeed;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.post.PostFilter;
+import org.jboss.blog.service.GroupsService;
+import org.jboss.blog.session.feed.InvalidFeedTypeException;
+import org.jboss.blog.session.security.tools.FeedSecurityTools;
+import org.jboss.blog.model.post.filter.AndFilter;
+import org.jboss.blog.tools.GeneralTools;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.annotations.security.Restrict;
+
+import javax.faces.application.FacesMessage;
+import javax.persistence.EntityManager;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.CONVERSATION)
+ at Name("aggregatedFeedMod")
+public class AggregatedFeedModBean implements Serializable {
+    @In
+    private FeedModBean feedMod;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private GroupsService groupsService;
+
+    private AggregatedFeed aggregatedFeed;
+
+    private List<Feed> availableFeeds;
+
+    private Map<Feed, Boolean> selectedFeeds;
+
+    private Map<Feed, List<PostFilter>> selectedFeedsFilters;
+
+    private List<Group> availableGroups;
+
+    private Map<Group, Boolean> selectedGroups;
+
+    private Map<Group, List<PostFilter>> selectedGroupsFilters;
+
+    private List<PostFilter> globalFilters;
+
+    public AggregatedFeed getAggregatedFeed() throws InvalidFeedTypeException {
+        if (aggregatedFeed == null) {
+            if (feedMod.getFeed() == null) {
+                aggregatedFeed = new AggregatedFeed();
+                aggregatedFeed.setFeeds(new HashMap<Feed, PostFilter>());
+                aggregatedFeed.setGroups(new HashMap<Group, PostFilter>());
+                aggregatedFeed.setGlobalFilter(new AndFilter());
+
+                feedMod.initNewFeed(aggregatedFeed);
+                aggregatedFeed.setLink(null);
+            } else {
+                if (feedMod.getFeed() instanceof AggregatedFeed) {
+                    aggregatedFeed = (AggregatedFeed) feedMod.getFeed();
+                } else {
+                    throw new InvalidFeedTypeException();
+                }
+            }
+        }
+
+        return aggregatedFeed;
+    }
+
+    private void fillPostFilterList(List<PostFilter> toFill, PostFilter source) {
+        if (source instanceof AndFilter) {
+            AndFilter andFilter = (AndFilter) source;
+            for (PostFilter filter : andFilter.getFilters()) {
+                toFill.add(filter);
+            }
+        }
+    }
+
+    private <T> void readSelectedEntities(List<T> availableEntities, Map<T, PostFilter> currentEntities,
+                                          Map<T, Boolean> selectedEntities,
+                                          Map<T, List<PostFilter>> selectedEntitiesFilters) {
+        // Populating individual filters
+        for (T entity : availableEntities) {
+            selectedEntitiesFilters.put(entity, new ArrayList<PostFilter>());
+
+            if (currentEntities.containsKey(entity)) {
+                fillPostFilterList(selectedEntitiesFilters.get(entity), currentEntities.get(entity));
+            }
+        }
+
+        // Populating selected entities
+        for (T entity : availableEntities) {
+            selectedEntities.put(entity, currentEntities.containsKey(entity));
+        }
+    }
+
+    @Create
+    public void populateLists() throws InvalidFeedTypeException {
+        // Populating available groups
+        availableGroups = groupsService.getAllGroups();
+        availableFeeds = new ArrayList<Feed>();
+
+        for (Group availableGroup : availableGroups) {
+            for (Feed nextFeed : groupsService.acceptedFeeds(availableGroup)) {
+                if (GeneralTools.objectsEqual(nextFeed, getAggregatedFeed())) {
+                    continue;
+                }
+
+                availableFeeds.add(nextFeed);
+            }
+
+            for (Feed nextFeed : groupsService.restrictedFeeds(availableGroup)) {
+                if (GeneralTools.objectsEqual(nextFeed, getAggregatedFeed())) {
+                    continue;
+                }
+
+                if (FeedSecurityTools.canViewFeed(nextFeed, true)) {
+                    availableFeeds.add(nextFeed);   
+                }
+            }
+        }
+
+        selectedFeedsFilters = new HashMap<Feed, List<PostFilter>>();
+        selectedFeeds = new HashMap<Feed, Boolean>();
+        readSelectedEntities(availableFeeds, getAggregatedFeed().getFeeds(), selectedFeeds, selectedFeedsFilters);
+
+        selectedGroupsFilters = new HashMap<Group, List<PostFilter>>();
+        selectedGroups = new HashMap<Group, Boolean>();
+        readSelectedEntities(availableGroups, getAggregatedFeed().getGroups(), selectedGroups, selectedGroupsFilters);
+
+        // Populating global filters
+        globalFilters = new ArrayList<PostFilter>();
+        fillPostFilterList(globalFilters, getAggregatedFeed().getGlobalFilter());
+    }
+
+    //
+
+    public List<Feed> getAvailableFeeds() {
+        return availableFeeds;
+    }
+
+    public Map<Feed, Boolean> getSelectedFeeds() {
+        return selectedFeeds;
+    }
+
+    public void setSelectedFeeds(Map<Feed, Boolean> selectedFeeds) {
+        this.selectedFeeds = selectedFeeds;
+    }
+
+    public Map<Feed, List<PostFilter>> getSelectedFeedsFilters() {
+        return selectedFeedsFilters;
+    }
+
+    //
+
+    public List<Group> getAvailableGroups() {
+        return availableGroups;
+    }
+
+    public Map<Group, Boolean> getSelectedGroups() {
+        return selectedGroups;
+    }
+
+    public void setSelectedGroups(Map<Group, Boolean> selectedGroups) {
+        this.selectedGroups = selectedGroups;
+    }
+
+    public Map<Group, List<PostFilter>> getSelectedGroupsFilters() {
+        return selectedGroupsFilters;
+    }
+
+    //
+
+    public List<PostFilter> getGlobalFilters() {
+        return globalFilters;
+    }
+
+    public void removeFilter(List<PostFilter> filters, PostFilter filter) {
+        filters.remove(filter);
+    }
+
+    private <T> void saveSelectedEntities(Map<T, PostFilter> currentEntities, Map<T, Boolean> selectedEntities,
+                                          Map<T, List<PostFilter>> selectedEntitiesFilters) {
+        currentEntities.clear();
+
+        for (T entity : selectedEntities.keySet()) {
+            if (selectedEntities.get(entity)) {
+                currentEntities.put(entity, new AndFilter(selectedEntitiesFilters.get(entity)));
+            }
+        }
+    }
+
+    private void save() throws InvalidFeedTypeException {
+        // Saving feeds and filters
+        saveSelectedEntities(getAggregatedFeed().getFeeds(), selectedFeeds, selectedFeedsFilters);
+
+        // Saving groups and filters
+        saveSelectedEntities(getAggregatedFeed().getGroups(), selectedGroups, selectedGroupsFilters);
+
+        // Saving global filters
+        getAggregatedFeed().setGlobalFilter(new AndFilter(globalFilters));
+    }
+
+    public void saveNew() throws InvalidFeedTypeException {
+        save();
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}")
+    public void saveExisting() throws InvalidFeedTypeException {
+        save();
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.aggregated.updated",
+                getAggregatedFeed().getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.feed.updated", getAggregatedFeed());
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/FeedModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/FeedModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/FeedModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,139 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.PostAuthorType;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Template;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.session.merge.MergeServiceBean;
+import org.jboss.blog.session.feed.lock.FeedsLocksBean;
+import org.jboss.blog.session.update.UpdateHandlerAsync;
+import org.jboss.blog.session.xml.velocity.TemplateServiceBean;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.faces.FacesMessages;
+
+import javax.faces.application.FacesMessage;
+import javax.persistence.EntityManager;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.CONVERSATION)
+ at Name("feedMod")
+ at AutoCreate
+public class FeedModBean implements Serializable {
+    private Feed feed;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private MergeServiceBean mergeService;
+
+    @In
+    private FeedsLocksBean feedsLocks;
+
+    @In
+    private UpdateHandlerAsync updateHandlerAsync;
+
+    @In
+    private TemplateServiceBean templateService;
+
+    @In
+    private Events events;
+
+    public void initNewFeed(Feed feed) {
+        this.feed = feed;
+        feed.setMaxPostsInFeed(10);
+        feed.setMaxPostsOnPage(10);
+        feed.setTemplates(new HashMap<XmlType, Template>());
+        feed.setAccepted(true);
+        feed.setPosts(new ArrayList<Post>());
+        feed.setPostAuthorType(PostAuthorType.BLOG_AUTHOR_IF_MISSING);
+
+        feed.getTemplates().put(XmlType.ATOM, templateService.templatesOfType(XmlType.ATOM).get(0));
+        feed.getTemplates().put(XmlType.RSS2, templateService.templatesOfType(XmlType.RSS2).get(0));
+        feed.getTemplates().put(XmlType.RSS1, templateService.templatesOfType(XmlType.RSS1).get(0));
+    }
+
+    public Feed getFeed() {
+        return feed;
+    }
+
+    public void setFeed(Feed feed) {
+        this.feed = feed;
+    }
+
+    public XmlType[] getTemplateTypes() {
+        return new XmlType[] { XmlType.ATOM, XmlType.RSS2 };
+    }
+
+    @Observer("org.jboss.blog.feed.accept")
+    public void acceptFeed() {
+        feed.setAccepted(true);
+
+        entityManager.flush();
+
+        updateHandlerAsync.update(feed);
+
+        events.raiseEvent("org.jboss.blog.feed.accepted", getFeed());
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.accepted", getFeed().getName());
+    }
+
+    @Observer("org.jboss.blog.feed.proposed")
+    public void proposedFeed() {
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.proposed", getFeed().getName());
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'add', feedMod.feed, feedMod.feed.group)}")
+    public void saveNew() {
+        Lock feedLock = feedsLocks.getLockForFeed(feed.getName());
+        feedLock.lock();
+        try {
+            entityManager.persist(feed);
+
+            for (Post post : feed.getPosts()) {
+                mergeService.savePost(feed, post);
+            }
+
+            entityManager.flush();
+        } finally {
+            feedLock.unlock();
+        }
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.added", getFeed().getName());
+
+        events.raiseEvent("org.jboss.blog.feed.added", getFeed());
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}")
+    public void saveExisting() {
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.updated", getFeed().getName());
+
+        events.raiseEvent("org.jboss.blog.feed.updated", getFeed());
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'delete', feedMod.feed, feedMod.feed.group)}")
+    public void delete() {
+        events.raiseEvent("org.jboss.blog.feed.before_delete", getFeed());
+
+        entityManager.remove(getFeed());
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.deleted", getFeed().getName());
+
+        events.raiseEvent("org.jboss.blog.feed.deleted", getFeed());
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/FilterAddBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/FilterAddBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/FilterAddBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,71 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.post.PostFilter;
+import org.jboss.blog.model.post.filter.PodcastFilter;
+import org.jboss.blog.model.post.filter.NotPodcastFilter;
+import org.jboss.blog.model.post.filter.CategoryRegexpFilter;
+
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("filterAdd")
+ at Scope(ScopeType.CONVERSATION)
+public class FilterAddBean implements Serializable {
+    private List<PostFilter> addToList;
+
+    private PodcastFilter podcastFilter;
+
+    private NotPodcastFilter notPodcastFilter;
+
+    private CategoryRegexpFilter authorRegexpFilter;
+
+    private CategoryRegexpFilter categoryRegexpFilter;
+
+    public PodcastFilter getPodcastFilter() {
+        if (podcastFilter == null) {
+            podcastFilter = new PodcastFilter();
+        }
+
+        return podcastFilter;
+    }
+
+    public NotPodcastFilter getNotPodcastFilter() {
+        if (notPodcastFilter == null) {
+            notPodcastFilter =  new NotPodcastFilter();
+        }
+
+        return notPodcastFilter;
+    }
+
+    public CategoryRegexpFilter getAuthorRegexpFilter() {
+        if (authorRegexpFilter == null) {
+            authorRegexpFilter = new CategoryRegexpFilter();
+        }
+        
+        return authorRegexpFilter;
+    }
+
+    public CategoryRegexpFilter getCategoryRegexpFilter() {
+        if (categoryRegexpFilter == null) {
+            categoryRegexpFilter = new CategoryRegexpFilter();
+        }
+        
+        return categoryRegexpFilter;
+    }
+
+    public void add(PostFilter filter) {
+        addToList.add(filter);
+    }
+
+    public void addToList(List<PostFilter> addToList) {
+        this.addToList = addToList;
+    }
+
+    public void cancel() { }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsDeleteListener.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsDeleteListener.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsDeleteListener.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,31 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.feed.HighlightsFeed;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("highlightsDeleteListener")
+ at Scope(ScopeType.STATELESS)
+public class HighlightsDeleteListener {
+    @In
+    private EntityManager entityManager;
+
+    @Observer("org.jboss.blog.post.before_delete")
+    public void beforePostDeleted(Post postToDelete) {
+        for (Object feedObj : entityManager.createQuery("select feed from HighlightsFeed feed").getResultList()) {
+            HighlightsFeed feed = (HighlightsFeed) feedObj;
+            feed.getSelectedPosts().remove(postToDelete);
+        }
+
+        entityManager.flush();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsFeedModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsFeedModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/HighlightsFeedModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,138 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.blog.model.feed.HighlightsFeed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.session.feed.InvalidFeedTypeException;
+import org.jboss.blog.tools.GeneralTools;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.faces.FacesMessages;
+
+import javax.faces.application.FacesMessage;
+import javax.persistence.EntityManager;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.CONVERSATION)
+ at Name("highlightsFeedMod")
+public class HighlightsFeedModBean implements Serializable {
+    @In
+    private FeedModBean feedMod;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    private HighlightsFeed highlightsFeed;
+
+    private Post post;
+
+    private HighlightsFeed selectedFeed;
+
+    private int[] positions;
+
+    private void initPositions(List<Post> posts) {
+        positions = new int[posts.size()];
+        for (int i=0; i<positions.length; i++) {
+            positions[i] = i;
+        }
+    }
+
+    public HighlightsFeed getHighlightsFeed() throws InvalidFeedTypeException {
+        if (highlightsFeed == null) {
+            if (feedMod.getFeed() == null) {
+                highlightsFeed = new HighlightsFeed();
+                feedMod.initNewFeed(highlightsFeed);
+
+                highlightsFeed.setSelectedPosts(new ArrayList<Post>());
+            } else {
+                if (feedMod.getFeed() instanceof HighlightsFeed) {
+                    highlightsFeed = (HighlightsFeed) feedMod.getFeed();
+                } else {
+                    throw new InvalidFeedTypeException();
+                }
+            }
+
+            initPositions(highlightsFeed.getSelectedPosts());
+        }
+
+        return highlightsFeed;
+    }
+
+    public HighlightsFeed getSelectedFeed() {
+        return selectedFeed;
+    }
+
+    public void setSelectedFeed(HighlightsFeed selectedFeed) {
+        this.selectedFeed = selectedFeed;
+    }
+
+    public Post getPost() {
+        return post;
+    }
+
+    public void setPost(Post post) {
+        this.post = post;
+    }
+
+    public int[] getPositions() {
+        return positions;
+    }
+
+    public void setPositions(int[] positions) {
+        this.positions = positions;
+    }
+
+    public void moveUp(int rowNumber) throws InvalidFeedTypeException {
+        GeneralTools.moveElement(getHighlightsFeed().getSelectedPosts(), rowNumber, rowNumber-1);
+    }
+
+    public void moveDown(int rowNumber) throws InvalidFeedTypeException {
+        GeneralTools.moveElement(getHighlightsFeed().getSelectedPosts(), rowNumber, rowNumber+1);
+    }
+
+    public void moveTo(int rowNumber) throws InvalidFeedTypeException {
+        GeneralTools.moveElement(getHighlightsFeed().getSelectedPosts(), rowNumber, positions[rowNumber]);
+        positions[rowNumber] = rowNumber;
+    }
+
+    public void delete(int rowNumber) throws InvalidFeedTypeException {
+        getHighlightsFeed().getSelectedPosts().remove(rowNumber);
+
+        initPositions(getHighlightsFeed().getSelectedPosts());
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}")
+    public void saveExisting() throws InvalidFeedTypeException {
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.highlights.updated",
+                getHighlightsFeed().getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.feed.updated", getHighlightsFeed());
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', highlightsFeedMod.selectedFeed, highlightsFeedMod.selectedFeed.group)}")
+    public void addPost() {
+        if (!getSelectedFeed().getSelectedPosts().contains(getPost())) {
+            getSelectedFeed().getSelectedPosts().add(getPost());
+
+            entityManager.flush();
+
+            facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.highlights.post.added",
+                    getPost().getTitle(), getSelectedFeed().getName());
+
+            Events.instance().raiseEvent("org.jboss.blog.feed.updated", getSelectedFeed());
+        }
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/IndividualFeedModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/IndividualFeedModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/IndividualFeedModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,291 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.blog.model.feed.IndividualPostsFeed;
+import org.jboss.blog.model.feed.IndividualPostInfo;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.session.feed.InvalidFeedTypeException;
+import org.jboss.blog.session.parser.ParserException;
+import org.jboss.blog.session.parser.ParserService;
+import org.jboss.blog.session.merge.MergeServiceBean;
+import org.jboss.blog.session.merge.PostManager;
+import org.jboss.blog.tools.GeneralTools;
+import org.jboss.blog.tools.StringTools;
+
+import javax.persistence.EntityManager;
+import javax.faces.application.FacesMessage;
+import javax.faces.model.SelectItem;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.CONVERSATION)
+ at Name("individualFeedMod")
+public class IndividualFeedModBean {
+    @In
+    private FeedModBean feedMod;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private Events events;
+
+    @In
+    private ParserService parserService;
+
+    @In
+    private PostsValidator postsValidator;
+
+    @In
+    private MergeServiceBean mergeService;
+
+    @In
+    private PostManager postManager;
+
+    private IndividualPostsFeed individualPostsFeed;
+
+    private String address;
+
+    private boolean parseOk;
+
+    private Feed parsedFeed;
+
+    private Post selectedPost;
+
+    private String selectedPostTitleAsId;
+
+    private String postAuthor;
+
+    private List<SelectItem> posts;
+
+    private List<IndividualPostInfo> postInfos;
+
+    private int from;
+
+    public IndividualPostsFeed getIndividualPostsFeed() throws InvalidFeedTypeException {
+        if (individualPostsFeed == null) {
+            if (feedMod.getFeed() == null) {
+                individualPostsFeed = new IndividualPostsFeed();
+                feedMod.initNewFeed(individualPostsFeed);
+
+                individualPostsFeed.setPostInfos(new ArrayList<IndividualPostInfo>());
+            } else {
+                if (feedMod.getFeed() instanceof IndividualPostsFeed) {
+                    individualPostsFeed = (IndividualPostsFeed) feedMod.getFeed();
+                } else {
+                    throw new InvalidFeedTypeException();
+                }
+            }
+        }
+
+        return individualPostsFeed;
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+    public boolean isParseOk() {
+        return parseOk;
+    }
+
+    public void setParseOk(boolean parseOk) {
+        this.parseOk = parseOk;
+    }
+
+    public Feed getParsedFeed() {
+        return parsedFeed;
+    }
+
+    public void setParsedFeed(Feed parsedFeed) {
+        this.parsedFeed = parsedFeed;
+    }
+
+    public Post getSelectedPost() {
+        return selectedPost;
+    }
+
+    public void setSelectedPost(Post selectedPost) {
+        this.selectedPost = selectedPost;
+    }
+
+    public String getSelectedPostTitleAsId() {
+        return selectedPostTitleAsId;
+    }
+
+    public int getFrom() {
+        return from;
+    }
+
+    public void setFrom(int from) {
+        if (this.from != from) {
+            postInfos = null;
+        }
+
+        this.from = from;
+    }
+
+    public int getPostsOnPage() {
+        return 10;
+    }
+
+    public boolean getShowPrevious() {
+        return from != 0;
+    }
+
+    public boolean getShowNext() throws InvalidFeedTypeException {
+        return getPostInfos().size() == getPostsOnPage()+1;
+    }
+
+    public int getPreviousFrom() {
+        return Math.max(0, from - getPostsOnPage());
+    }
+
+    public int getNextFrom() {
+        return from + getPostsOnPage();
+    }
+
+    public List<IndividualPostInfo> getPostInfos() throws InvalidFeedTypeException {
+        if (postInfos == null) {
+            //noinspection unchecked
+            postInfos = entityManager
+                    .createQuery("SELECT pi FROM IndividualPostInfo pi WHERE pi.feed = ?1 ORDER BY pi.post.published DESC")
+                    .setParameter(1, getIndividualPostsFeed())
+                    .setFirstResult(from)
+                    .setMaxResults(getPostsOnPage()+1)
+                    .getResultList();
+        }
+
+        return postInfos;
+    }
+
+    public void setPostInfos(List<IndividualPostInfo> postInfos) {
+        this.postInfos = postInfos;
+    }
+
+    public void setSelectedPostTitleAsId(String selectedPostTitleAsId) {
+        this.selectedPostTitleAsId = selectedPostTitleAsId;
+
+        if (parsedFeed != null) {
+            for (Post post : parsedFeed.getPosts()) {
+                if (GeneralTools.objectsEqual(post.getTitleAsId(), selectedPostTitleAsId)) {
+                    selectedPost = post;
+                    break;
+                }
+            }
+        }
+    }
+
+    public String getPostAuthor() {
+        return postAuthor;
+    }
+
+    public void setPostAuthor(String postAuthor) {
+        this.postAuthor = postAuthor;
+    }
+
+    public List<SelectItem> getPosts() {
+        return posts;
+    }
+
+    public void setPosts(List<SelectItem> posts) {
+        this.posts = posts;
+    }
+
+    public void updateSelectedPost() {
+        if (!StringTools.isEmpty(selectedPost.getAuthor())) {
+            postAuthor = selectedPost.getAuthor();
+        }
+    }
+
+    public void parseFeed() {
+        try {
+            parsedFeed = parserService.parse(getAddress());
+
+            if (!postsValidator.validatePosts(parsedFeed.getPosts(), true, "address")) {
+                throw new ParserException("Posts are missing some information.");
+            }
+
+            postAuthor = parsedFeed.getAuthor();
+
+            posts = new ArrayList<SelectItem>();
+            for (Post post : parsedFeed.getPosts()) {
+                posts.add(new SelectItem(post.getTitleAsId(), post.getTitle()));
+            }
+
+            if (parsedFeed.getPosts().size() > 0) {
+                selectedPost = parsedFeed.getPosts().get(0);
+                updateSelectedPost();
+            }
+
+            setParseOk(true);
+        } catch (ParserException e) {
+            facesMessages.addToControlFromResourceBundle("parse", FacesMessage.SEVERITY_ERROR,
+                    "blog.feed.remote.parsenotok", e.getMessage());
+
+            setParseOk(false);
+        }
+    }
+
+    public void reset() {
+        parseOk = false;
+        address = "";
+        selectedPost = null;
+        selectedPostTitleAsId = null;
+        postInfos = null;
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}")
+    public void addPost() throws InvalidFeedTypeException {
+        selectedPost.setAuthor(postAuthor);
+
+        mergeService.savePost(individualPostsFeed, selectedPost);
+
+        IndividualPostInfo postInfo = new IndividualPostInfo();
+        postInfo.setPost(selectedPost);
+        postInfo.setFeed(individualPostsFeed);
+        postInfo.setRemoteFeedAddress(address);
+
+        entityManager.persist(postInfo);
+
+        individualPostsFeed.getPostInfos().add(postInfo);
+
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.individual.post.added",
+                selectedPost.getTitle());
+
+        events.raiseEvent("org.jboss.blog.feed.updated", individualPostsFeed);
+
+        reset();
+    }
+
+    public void delete(IndividualPostInfo postInfo) throws InvalidFeedTypeException {
+        individualPostsFeed.getPostInfos().remove(postInfo);
+        entityManager.remove(postInfo);
+        postManager.deletePost(postInfo.getPost());
+
+        entityManager.flush();
+
+        events.raiseEvent("org.jboss.blog.feed.updated", individualPostsFeed);
+
+        reset();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/PostsValidator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/PostsValidator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/PostsValidator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,99 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.core.Validators;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Enclosure;
+import org.jboss.blog.model.Image;
+import org.hibernate.validator.InvalidValue;
+import org.hibernate.validator.ClassValidator;
+
+import javax.faces.application.FacesMessage;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.STATELESS)
+ at Name("postsValidator")
+ at AutoCreate
+public class PostsValidator {
+    @In
+    private Validators validators;
+
+    @In
+    private FacesMessages facesMessages;
+
+    public boolean validatePosts(List<Post> posts, boolean checkLinks, String controlForMessages)  {
+        ClassValidator<Post> postValidator = validators.getValidator(Post.class);
+        ClassValidator<Enclosure> enclosureValidator = validators.getValidator(Enclosure.class);
+        ClassValidator<Image> imageValidator = validators.getValidator(Image.class);
+
+        for (Post post : posts) {
+            InvalidValue[] invalidValues = postValidator.getInvalidValues(post);
+            if (invalidValues.length != 0) {
+                boolean validationFailed = false;
+
+                for (InvalidValue invalidValue : invalidValues) {
+                    if ((checkLinks || !"link".equals(invalidValue.getPropertyName())) &&
+                            (!"feed".equals(invalidValue.getPropertyName()))) {
+                        validationFailed = true;
+
+                        facesMessages.addToControlFromResourceBundle(controlForMessages, FacesMessage.SEVERITY_ERROR,
+                                "blog.feed.post.invalid", post.getTitle(),
+                                invalidValue.getPropertyName(), invalidValue.getMessage());
+                    }
+                }
+
+                return !validationFailed;
+            }
+
+            for (Enclosure enc : post.getEnclosures()) {
+                invalidValues = enclosureValidator.getInvalidValues(enc);
+
+                if (invalidValues.length != 0) {
+                    boolean validationFailed = false;
+
+                    for (InvalidValue invalidValue : invalidValues) {
+                        if (!"post".equals(invalidValue.getPropertyName())) {
+                            validationFailed = true;
+
+                            facesMessages.addToControlFromResourceBundle(controlForMessages, FacesMessage.SEVERITY_ERROR,
+                                    "blog.feed.enclosure.invalid", post.getTitle(), enc.getUrl(),
+                                    invalidValue.getPropertyName(), invalidValue.getMessage());
+                        }
+                    }
+
+                    return !validationFailed;
+                }
+            }
+
+            for (Image image : post.getImages()) {
+                invalidValues = imageValidator.getInvalidValues(image);
+
+                if (invalidValues.length != 0) {
+                    boolean validationFailed = false;
+
+                    for (InvalidValue invalidValue : invalidValues) {
+                        if (!"post".equals(invalidValue.getPropertyName())) {
+                            validationFailed = true;
+
+                            facesMessages.addToControlFromResourceBundle(controlForMessages, FacesMessage.SEVERITY_ERROR,
+                                    "blog.feed.enclosure.invalid", post.getTitle(),
+                                    invalidValue.getPropertyName(), invalidValue.getMessage());
+                        }
+                    }
+
+                    return !validationFailed;
+                }
+            }
+        }
+
+        return true;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsListener.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsListener.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsListener.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,83 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Logger;
+import org.jboss.seam.faces.Renderer;
+import org.jboss.seam.log.Log;
+import org.jboss.blog.model.log.PropositionsLog;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.session.security.FeedsIdentity;
+import org.jboss.blog.session.configuration.ConfigurationManager;
+import org.jboss.blog.tools.StringTools;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("propositionsListener")
+public class PropositionsListener {
+    @In
+    private FeedModBean feedMod;
+
+    @In
+    private EntityManager entityManager;
+
+    @In(create = true)
+    private Renderer renderer;
+
+    @In
+    private FeedsIdentity identity;
+
+    @In
+    private ConfigurationManager configurationManager;
+
+    @In
+    private PropositionsToolsBean propositionsTools;
+
+    @Logger
+    private Log log;
+
+    private String currentEmail;
+
+    public String getCurrentEmail() {
+        return currentEmail;
+    }
+
+    private void sendEmail(String email, String view) {
+        if (!StringTools.isEmpty(email)) {
+            try {
+                currentEmail = email;
+                renderer.render(view);
+            } catch (Exception e) {
+                log.warn(e);
+            }
+        }
+    }
+
+    @Observer("org.jboss.blog.feed.proposed")
+    public void feedProposed() {
+        entityManager.persist(new PropositionsLog(feedMod.getFeed(), identity.getSecurityUser(), new Date()));
+
+        sendEmail(feedMod.getFeed().getGroup().getAdminEmail(), "/emails/new_proposition_email.xhtml");
+        sendEmail(configurationManager.getConfiguration().getAdminEmail(), "/emails/new_proposition_email.xhtml");
+    }
+
+    @Observer("org.jboss.blog.feed.accepted")
+    public void feedAccepted(Feed feed) {
+        sendEmail(propositionsTools.getEmailForProposedFeed(feed), "/emails/proposition_accepted.xhtml");
+    }
+
+    @Observer("org.jboss.blog.feed.before_delete")
+    public void beforeFeedDeleted(Feed feed) {
+        if (!feed.isAccepted()) {
+            sendEmail(propositionsTools.getEmailForProposedFeed(feed), "/emails/proposition_rejected.xhtml");
+        }
+
+        entityManager.createQuery("delete from PropositionsLog pl where pl.feed = ?1")
+                .setParameter(1, feed).executeUpdate();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsToolsBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsToolsBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/PropositionsToolsBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,62 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.service.GroupsService;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.security.SecurityUser;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.session.security.filtering.GroupsSecurity;
+import org.jboss.blog.session.security.external.ExternalSecurityService;
+
+import javax.persistence.NoResultException;
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("propositionsTools")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class PropositionsToolsBean {
+    @In
+    private GroupsService groupsService;
+
+    @In
+    private GroupsSecurity groupsSecurity;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private ExternalSecurityService externalSecurityService;
+
+    public int getPendingPropositions() {
+        int total = 0;
+
+        for (Group group : groupsSecurity.filterForFeedMod(groupsService.getAllGroups())) {
+            total += groupsService.unacceptedFeeds(group).size();
+        }
+
+        return total;
+    }
+
+    public String getEmailForProposedFeed(Feed feed) {
+        try {
+            SecurityUser securityUser = (SecurityUser) entityManager.createQuery(
+                    "select pl.securityUser from PropositionsLog pl where pl.feed = ?1")
+                    .setParameter(1, feed).getSingleResult();
+
+            if (securityUser != null) {
+                return externalSecurityService.getEmail(securityUser);
+            } else {
+                return null;
+            }
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/feed/mod/RemoteFeedModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/mod/RemoteFeedModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/mod/RemoteFeedModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,261 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.RemoteFeed;
+import org.jboss.blog.model.feed.PostAuthorType;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Category;
+import org.jboss.blog.session.feed.InvalidFeedTypeException;
+import org.jboss.blog.session.parser.ParserException;
+import org.jboss.blog.session.parser.ParserService;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.blog.tools.PostFilterTools;
+import org.jboss.blog.tools.validator.Regexp;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.faces.FacesMessages;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.model.SelectItem;
+import javax.persistence.EntityManager;
+import java.io.Serializable;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.regex.Pattern;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.CONVERSATION)
+ at Name("remoteFeedMod")
+public class RemoteFeedModBean implements Serializable {
+    @In
+    private ParserService parserService;
+
+    @In
+    private FeedModBean feedMod;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private PostsValidator postsValidator;
+
+    private List<SelectItem> includeCategories;
+
+    private String includeCategory;
+
+    @Regexp
+    private String includeOtherCategoryRegexp;
+
+    private List<Post> visiblePosts;
+
+    private RemoteFeed remoteFeed;
+
+    private Feed parsedFeed;
+
+    private boolean parseOk;
+
+    private final static String ALL_CATEGORIES = "ALL";
+    private final static String OTHER_CATEGORY = "OTHER";
+
+    public RemoteFeed getRemoteFeed() throws InvalidFeedTypeException {
+        if (remoteFeed == null) {
+            if (feedMod.getFeed() == null) {
+                remoteFeed = new RemoteFeed();
+                feedMod.initNewFeed(remoteFeed);
+
+                if (StringTools.isEmpty(remoteFeed.getLink())) {
+                    remoteFeed.setLink(remoteFeed.getRemoteLink());
+                }
+            } else {
+                if (feedMod.getFeed() instanceof RemoteFeed) {
+                    remoteFeed = (RemoteFeed) feedMod.getFeed();
+                    if (StringTools.isEmpty(remoteFeed.getIncludeCategoryRegexp())) {
+                        includeCategory = ALL_CATEGORIES;
+                    } else {
+                        includeCategory = OTHER_CATEGORY;
+                        includeOtherCategoryRegexp = remoteFeed.getIncludeCategoryRegexp();
+                    }
+
+                    initIncludeCategories();
+                    finishIncludeCategories();
+                } else {
+                    throw new InvalidFeedTypeException();
+                }
+            }
+        }
+
+        return remoteFeed;
+    }
+
+    public boolean isParseOk() {
+        return parseOk;
+    }
+
+    public void setParseOk(boolean parseOk) {
+        this.parseOk = parseOk;
+    }
+
+    public List<SelectItem> getIncludeCategories() {
+        return includeCategories;
+    }
+
+    public String getIncludeCategory() {
+        return includeCategory;
+    }
+
+    public void setIncludeCategory(String includeCategory) throws InvalidFeedTypeException {
+        this.includeCategory = includeCategory;
+    }
+
+    public String getIncludeOtherCategoryRegexp() {
+        return includeOtherCategoryRegexp;
+    }
+
+    public void setIncludeOtherCategoryRegexp(String includeOtherCategoryRegexp) throws InvalidFeedTypeException {
+        this.includeOtherCategoryRegexp = includeOtherCategoryRegexp;
+    }
+
+    public List<Post> getVisiblePosts() {
+        return visiblePosts;
+    }
+
+    private void generateIncludedCategory() throws InvalidFeedTypeException {
+        if (ALL_CATEGORIES.equals(includeCategory)) {
+            getRemoteFeed().setIncludeCategoryRegexp(null);
+        } else if (OTHER_CATEGORY.equals(includeCategory)) {
+            getRemoteFeed().setIncludeCategoryRegexp(includeOtherCategoryRegexp);
+        } else {
+            getRemoteFeed().setIncludeCategoryRegexp(
+                    includeCategory == null ? null : Pattern.quote(includeCategory));
+        }
+    }
+
+    public void generateVisiblePosts() throws InvalidFeedTypeException {
+        generateIncludedCategory();
+
+        RemoteFeed remoteFeed = getRemoteFeed();        
+        visiblePosts = new ArrayList<Post>();
+
+        if (parsedFeed != null) {
+            for (Post post : parsedFeed.getPosts()) {
+                if (remoteFeed.getIncludeCategoryFilter().filter(post)) {
+                    visiblePosts.add(post);
+                }
+            }
+        }
+    }
+
+    public void unsetAccepted() throws InvalidFeedTypeException {
+        getRemoteFeed().setAccepted(false);
+    }
+
+    private void initIncludeCategories() {
+        includeCategories = new ArrayList<SelectItem>();
+        includeCategories.add(new SelectItem(ALL_CATEGORIES, "All categories"));
+    }
+
+    private void finishIncludeCategories() {
+        includeCategories.add(new SelectItem(OTHER_CATEGORY, "Other (regular expression) ..."));
+    }
+
+    private Set<Category> getCategoriesOfPosts(Feed feed) {
+        Set<Category> categories = new HashSet<Category>();
+
+        for (Post post : feed.getPosts()) {
+            categories.addAll(post.getCategories());
+        }
+
+        return categories;
+    }
+
+    public void parseFeed() throws InvalidFeedTypeException {
+        try {
+            parsedFeed = parserService.parse(getRemoteFeed().getRemoteLink());
+
+            if (!postsValidator.validatePosts(parsedFeed.getPosts(), true, "link")) {
+               throw new ParserException("Posts are missing some information.");
+            }
+
+            initIncludeCategories();
+            Set<Category> categories = getCategoriesOfPosts(parsedFeed);
+            for (Category cat : categories) {
+                includeCategories.add(new SelectItem(cat.getName(), cat.getName()));    
+            }
+            finishIncludeCategories();
+
+            generateVisiblePosts();
+
+            setParseOk(true);
+
+            if (categories.size() == 0) {
+                facesMessages.addToControlFromResourceBundle("parse", FacesMessage.SEVERITY_INFO,
+                        "blog.feed.remote.parseok.nocategories");
+            } else {
+                facesMessages.addToControlFromResourceBundle("parse", FacesMessage.SEVERITY_INFO,
+                        "blog.feed.remote.parseok.categories");
+            }
+        } catch (ParserException e) {
+            facesMessages.addToControlFromResourceBundle("parse", FacesMessage.SEVERITY_ERROR,
+                        "blog.feed.remote.parsenotok", e.getMessage());
+
+            setParseOk(false);
+        }
+    }
+
+    public void saveNew() throws InvalidFeedTypeException {
+        RemoteFeed remoteFeed = getRemoteFeed();
+        remoteFeed.setAuthor(parsedFeed.getAuthor());
+        remoteFeed.setDescription(parsedFeed.getDescription());
+        remoteFeed.setLink(parsedFeed.getLink());
+        remoteFeed.setTitle(parsedFeed.getTitle());
+
+        generateIncludedCategory();
+
+        if (getRemoteFeed().isAccepted()) {
+            remoteFeed.setPosts(PostFilterTools.filterPostList(parsedFeed.getPosts(),
+                    remoteFeed.getIncludeCategoryFilter()));
+        }
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}")
+    public void saveExisting() throws InvalidFeedTypeException {
+        getRemoteFeed().setLink(parsedFeed.getLink());
+
+        generateIncludedCategory();
+
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.remote.updated",
+                getRemoteFeed().getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.feed.updated", getRemoteFeed());
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}")
+    public void savePartial() throws InvalidFeedTypeException {
+        PostAuthorType newPostAuthorType = getRemoteFeed().getPostAuthorType();
+
+        entityManager.refresh(getRemoteFeed());
+
+        getRemoteFeed().setPostAuthorType(newPostAuthorType);
+        generateIncludedCategory();
+
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.remote.updated",
+                getRemoteFeed().getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.feed.updated", getRemoteFeed());
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedPosts.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedPosts.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedPosts.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,111 @@
+package org.jboss.blog.session.feed.posts;
+
+import org.jboss.blog.model.feed.AggregatedFeed;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.GroupsService;
+import org.jboss.blog.tools.GeneralTools;
+import org.jboss.blog.tools.PostFilterTools;
+import org.jboss.blog.model.post.PostFilter;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("aggregatedFeedPosts")
+ at Scope(ScopeType.STATELESS)
+public class AggregatedFeedPosts {
+    @In
+    private FeedsService feedsService;
+
+    @In
+    private GroupsService groupsService;
+
+    @In
+    private AggregatedFeedStack aggregatedFeedStack;
+
+    @SuppressWarnings("unchecked")
+    public List<? extends RestrictedPost> getPosts(AggregatedFeed aggregatedFeed, int from, int to,
+                                                   boolean restricted) {
+        if (aggregatedFeedStack.contains(aggregatedFeed)) {
+            return null;
+        }
+
+        // Adding this feed to the stack, so it's posts won't be read again.
+        aggregatedFeedStack.push(aggregatedFeed);
+
+        Map<Feed, PostFilter> feedsAndFilters = new HashMap<Feed, PostFilter>(aggregatedFeed.getFeeds());
+
+        Map<Group, PostFilter> groupsAndFilters = aggregatedFeed.getGroups();
+        for (Group group : groupsAndFilters.keySet()) {
+            PostFilter groupFilter = groupsAndFilters.get(group);
+            for (Feed feedInGroup : groupsService.acceptedFeeds(group)) {
+                feedsAndFilters.put(feedInGroup, groupFilter);
+            }
+
+            if (restricted) {
+                for (Feed feedInGroup : groupsService.restrictedFeeds(group)) {
+                    feedsAndFilters.put(feedInGroup, groupFilter);
+                }
+            }
+        }
+
+        PostFilter globalFilter = aggregatedFeed.getGlobalFilter();
+
+        // Initial list of aggregated feeds.
+        Set<Feed> availableFeeds = feedsAndFilters.keySet();
+
+        // Generating the list of posts.
+        SortedSet<RestrictedPost> posts = new TreeSet<RestrictedPost>();
+
+        // Bounds for the number of posts read from each feed.
+        int feedPostsFrom = 0;
+        int feedPostsTo = to;
+
+        do {
+            // A list of feeds from which we may later read more posts, if necessary.
+            Set<Feed> newAvailableFeeds = new HashSet<Feed>();
+
+            for (Feed feed : availableFeeds) {
+                List<? extends RestrictedPost> feedPosts = feedsService.getPosts(feed, feedPostsFrom, feedPostsTo,
+                        restricted);
+
+                if (feedPosts == null) {
+                    continue;
+                }
+
+                // Checking if this feed has any more posts; if so, remembering it so it can be checked for more posts,
+                // if needed.
+                if (feedPosts.size() == feedPostsTo-feedPostsFrom) {
+                    newAvailableFeeds.add(feed);
+                }
+
+                // Apply the local filters
+                PostFilterTools.filterFromPostList(feedPosts, feedsAndFilters.get(feed));
+
+                // Apply the global filters
+                PostFilterTools.filterFromPostList(feedPosts, globalFilter);
+
+                posts.addAll(feedPosts);
+            }
+
+            availableFeeds = newAvailableFeeds;
+
+            feedPostsFrom = feedPostsTo;
+            feedPostsTo += to;
+        } while (availableFeeds.size() != 0 && posts.size() < to);
+
+        // Popping this feed from the stack.
+        aggregatedFeedStack.pop();
+
+        // Cut the list to the desired length
+        return GeneralTools.subList(new ArrayList(posts), from, to);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedStack.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedStack.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/posts/AggregatedFeedStack.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,39 @@
+package org.jboss.blog.session.feed.posts;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.feed.AggregatedFeed;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("aggregatedFeedStack")
+ at Scope(ScopeType.CONVERSATION)
+ at AutoCreate
+public class AggregatedFeedStack implements Serializable {
+    private List<AggregatedFeed> stack;
+
+    @PostConstruct
+    public void initStack() {
+        stack = new ArrayList<AggregatedFeed>();
+    }
+
+    public void push(AggregatedFeed feed) {
+        stack.add(feed);
+    }
+
+    public void pop() {
+        stack.remove(stack.size()-1);
+    }
+
+    public boolean contains(AggregatedFeed feed) {
+        return stack.contains(feed);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/posts/DatabaseFeedPosts.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/posts/DatabaseFeedPosts.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/posts/DatabaseFeedPosts.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,40 @@
+package org.jboss.blog.session.feed.posts;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.feed.Feed;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("databaseFeedPosts")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class DatabaseFeedPosts {
+    @In
+    private EntityManager entityManager;
+
+    @SuppressWarnings("unchecked")
+    public List<? extends RestrictedPost> getPosts(Feed feed, int from, int to) {
+        return (List<Post>) entityManager.createQuery(
+                "select post from Post post where post.feed = ?1 order by post.published desc, post.link")
+                .setParameter(1, feed).setMaxResults(to-from).setFirstResult(from).getResultList();
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<? extends RestrictedPost> getPosts(int from, int to) {
+        return (List<Post>) entityManager.createQuery(
+                "select post from Post post where (post.feed.restricted is null or post.feed.restricted = false) " +
+                        "order by post.published desc, post.link")
+                .setMaxResults(to-from).setFirstResult(from)
+                .setHint("org.hibernate.cacheable", Boolean.TRUE).getResultList();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedType.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedType.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedType.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,22 @@
+package org.jboss.blog.session.feed.type;
+
+import org.jboss.blog.model.feed.Feed;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+public @interface FeedType {
+    public String name();
+    public Class<? extends Feed> model();
+    public String addPage();
+    public String editPage();
+
+    //public String feedUpdateComponentName();
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedTypes.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedTypes.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/type/FeedTypes.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,96 @@
+package org.jboss.blog.session.feed.type;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.blog.session.feed.InvalidFeedTypeException;
+import org.jboss.blog.session.feed.dao.FeedDao;
+import org.jboss.blog.session.scanner.ClassHandler;
+import org.jboss.blog.tools.KeySafeMap;
+import org.jboss.blog.tools.KeyNotMappedException;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.annotations.*;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("feedTypes")
+ at Scope(ScopeType.APPLICATION)
+ at AutoCreate
+public class FeedTypes implements ClassHandler {
+    @Logger
+    private Log log;
+
+    private Map<Class<? extends RestrictedFeed>, FeedType> feedTypes;
+    private Map<Class<? extends RestrictedFeed>, Constructor<? extends FeedDao>> feedDaos;
+
+    public FeedTypes() {
+        feedTypes = KeySafeMap.wrap(new ConcurrentHashMap<Class<? extends RestrictedFeed>, FeedType>());
+        feedDaos = KeySafeMap.wrap(new ConcurrentHashMap<Class<? extends RestrictedFeed>,
+                Constructor<? extends FeedDao>>());
+    }
+
+    public FeedType[] getAllTypes() {
+        return feedTypes.values().toArray(new FeedType[feedTypes.size()]);
+    }
+
+    public FeedType getFeedType(Feed feed) throws InvalidFeedTypeException {
+        try {
+            return feedTypes.get(feed.getClass());
+        } catch (KeyNotMappedException e) {
+            throw new InvalidFeedTypeException(feed.getClass().getName());
+        }
+    }
+
+    public FeedDao getFeedDao(RestrictedFeed feed) throws InvalidFeedTypeException {
+        try {
+            return feedDaos.get(feed.getClass()).newInstance(feed);
+        } catch (InstantiationException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException(e);
+        } catch (KeyNotMappedException e) {
+            throw new InvalidFeedTypeException(feed.getClass().getName());
+        }
+    }
+
+    private void registerDao(Class<? extends FeedDao> daoClass) {
+        FeedType feedType = daoClass.getAnnotation(FeedType.class);
+
+        if (feedType == null) {
+            throw new IllegalArgumentException(
+                    "Feed dao class: " + daoClass.getName() + " isn't annotated with @FeedType.");
+        }
+
+        Constructor<? extends FeedDao> constructor;
+        try {
+            constructor = daoClass.getConstructor(feedType.model());
+        } catch (NoSuchMethodException e) {
+            log.error("Feed dao class: " + daoClass.getName() + " doesn't define a one-arg constructor (" +
+                    feedType.model().getName() + "), as the @FeedType annotation states.");
+            return;
+        }
+
+        log.info("Registering feed dao class: #0.", daoClass.getName());
+
+        feedDaos.put(feedType.model(), constructor);
+        feedTypes.put(feedType.model(), feedType);
+    }
+
+    public void handleClass(Class<?> toHandle) {
+        if (FeedDao.class.isAssignableFrom(toHandle)) {
+            //noinspection unchecked
+            registerDao((Class<? extends FeedDao>) toHandle);
+        } else {
+            log.error("Class " + toHandle.getName() + " is annotated with @FeedType but doesn't implement the " +
+                    "FeedDao interface.");
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/update/IndividualPostsFeedUpdate.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/update/IndividualPostsFeedUpdate.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/update/IndividualPostsFeedUpdate.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,76 @@
+package org.jboss.blog.session.feed.update;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.session.parser.ParserService;
+import org.jboss.blog.session.parser.ParserException;
+import org.jboss.blog.session.merge.MergeServiceBean;
+import org.jboss.blog.session.feed.lock.FeedsLocksBean;
+import org.jboss.blog.session.update.UpdateException;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.IndividualPostsFeed;
+import org.jboss.blog.model.feed.IndividualPostInfo;
+import org.jboss.blog.model.Post;
+
+import javax.persistence.EntityManager;
+import java.util.concurrent.locks.Lock;
+import java.util.List;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("individualPostsFeedUpdate")
+ at Scope(ScopeType.STATELESS)
+public class IndividualPostsFeedUpdate {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private ParserService parserService;
+
+    @In
+    private MergeServiceBean mergeService;
+
+    @In
+    private FeedsLocksBean feedsLocks;
+
+    public void update(IndividualPostsFeed feed) throws UpdateException {
+        // We update only posts from the last week.
+        Date thresholdDate = new Date(System.currentTimeMillis() - 7*24*60*60*1000);
+
+        Lock feedLock = feedsLocks.getLockForFeed(feed.getName());
+        feedLock.lock();
+        try {
+            //noinspection unchecked
+            List<IndividualPostInfo> postInfosToUpdate = entityManager
+                    .createQuery(
+                            "SELECT pi FROM IndividualPostInfo pi WHERE pi.feed = ?1 AND pi.post.published > ?2")
+                    .setParameter(1, feed)
+                    .setParameter(2, thresholdDate)
+                    .getResultList();
+
+            for (IndividualPostInfo ipi : postInfosToUpdate) {
+                // Parsing the feed
+                Feed parsedFeed;
+                try {
+                    parsedFeed = parserService.parse(ipi.getRemoteFeedAddress());
+                } catch (ParserException e) {
+                    throw new UpdateException(e);
+                }
+
+                // Searching for the post
+                for (Post parsedPost : parsedFeed.getPosts()) {
+                    if (parsedPost.compareTo(ipi.getPost()) == 0) {
+                        mergeService.mergePosts(ipi.getPost(), parsedPost);
+                        break;
+                    }
+                }
+            }
+        } finally {
+            feedLock.unlock();
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/feed/update/RemoteFeedUpdate.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/feed/update/RemoteFeedUpdate.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/feed/update/RemoteFeedUpdate.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,50 @@
+package org.jboss.blog.session.feed.update;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.RemoteFeed;
+import org.jboss.blog.session.parser.ParserException;
+import org.jboss.blog.session.parser.ParserService;
+import org.jboss.blog.session.merge.MergeServiceBean;
+import org.jboss.blog.session.update.UpdateException;
+import org.jboss.blog.session.feed.lock.FeedsLocksBean;
+import org.jboss.blog.tools.PostFilterTools;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+import java.util.concurrent.locks.Lock;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("remoteFeedUpdate")
+ at Scope(ScopeType.STATELESS)
+public class RemoteFeedUpdate {
+    @In
+    private ParserService parserService;
+
+    @In
+    private MergeServiceBean mergeService;
+    
+    @In
+    private FeedsLocksBean feedsLocks;
+
+    public void update(RemoteFeed feed) throws UpdateException {
+        Lock feedLock = feedsLocks.getLockForFeed(feed.getName());
+        feedLock.lock();
+        try {
+            Feed parsedFeed;
+            try {
+                parsedFeed = parserService.parse(feed.getRemoteLink());
+            } catch (ParserException e) {
+                throw new UpdateException(e);
+            }
+
+            mergeService.merge(feed, PostFilterTools.filterPostList(parsedFeed.getPosts(),
+                    feed.getIncludeCategoryFilter()));
+        } finally {
+            feedLock.unlock();
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/group/GroupModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/group/GroupModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/group/GroupModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,98 @@
+package org.jboss.blog.session.group;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.service.GroupsService;
+import org.jboss.blog.tools.StringTools;
+
+import javax.persistence.EntityManager;
+import javax.faces.application.FacesMessage;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("groupMod")
+ at Scope(ScopeType.CONVERSATION)
+public class GroupModBean implements Serializable {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private GroupsService groupsService;
+
+    private Group group;
+
+    public Group getGroup() {
+        if (group == null) {
+            group = new Group();
+        }
+
+        return group;
+    }
+
+    public void setGroup(Group group) {
+        this.group = group;
+    }
+
+    @Restrict("#{identity.hasPermission('group', 'add')}")
+    public void saveNew() {
+        group.setHeader(StringTools.fixHtml(group.getHeader()));
+
+        entityManager.persist(group);
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.group.added", group.getDisplayName(),
+                group.getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.group.added", group);
+    }
+
+    @Restrict("#{identity.hasPermission('group', 'edit', groupMod.group)}")
+    public void saveExisting() {
+        group.setHeader(StringTools.fixHtml(group.getHeader()));
+        
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.group.updated", group.getDisplayName(),
+                group.getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.group.updated", group);
+    }
+
+    @Restrict("#{identity.hasPermission('group', 'delete', groupMod.group)}")
+    public void delete() {
+        if ((groupsService.acceptedFeeds(group).size() > 0) || (groupsService.unacceptedFeeds(group).size() > 0)) {
+            facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.group.cannotdelete",
+                    group.getDisplayName(), group.getName());
+
+            return;
+        }
+
+        Events.instance().raiseEvent("org.jboss.blog.group.before_delete", group);
+
+        entityManager.remove(group);
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.group.deleted", group.getDisplayName(),
+                group.getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.group.deleted", group);
+    }
+
+    // Marker actions
+
+    public void add() { }
+
+    public void edit() { }
+
+    public void cancel() { }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/group/GroupsServiceImpl.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/group/GroupsServiceImpl.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/group/GroupsServiceImpl.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,60 @@
+package org.jboss.blog.session.group;
+
+import org.jboss.blog.service.GroupsService;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.ScopeType;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.STATELESS)
+ at Name("groupsService")
+ at AutoCreate
+public class GroupsServiceImpl implements GroupsService {
+    @In
+    private EntityManager entityManager;
+
+    @SuppressWarnings("unchecked")
+    public List<Group> getAllGroups() {
+        return entityManager.createQuery("select group from Group group order by group.name")
+                .setHint("org.hibernate.cacheable", Boolean.TRUE).getResultList();
+    }
+
+    public List<Feed> acceptedFeeds(Group group) {
+        //noinspection unchecked
+        return entityManager
+                .createQuery("select feed from Feed feed where feed.group = ?1 and feed.accepted = true " +
+                        "and (feed.restricted is null or feed.restricted = false)")
+                .setParameter(1, group).setHint("org.hibernate.cacheable", Boolean.TRUE).getResultList();
+    }
+
+    public List<Feed> unacceptedFeeds(Group group) {
+        //noinspection unchecked
+        return entityManager
+                .createQuery("select feed from Feed feed where feed.group = ?1 and not (feed.accepted = true) " +
+                        "and (feed.restricted is null or feed.restricted = false)")
+                .setParameter(1, group).setHint("org.hibernate.cacheable", Boolean.TRUE).getResultList();
+    }
+
+    public List<Feed> restrictedFeeds(Group group) {
+        //noinspection unchecked
+        return entityManager
+                .createQuery("select feed from Feed feed where feed.group = ?1 and feed.restricted = true")
+                .setParameter(1, group).setHint("org.hibernate.cacheable", Boolean.TRUE).getResultList();
+    }
+
+    public List<Feed> allAcceptedFeeds(Group group) {
+        //noinspection unchecked
+        return entityManager
+                .createQuery("select feed from Feed feed where feed.group = ?1 and feed.accepted = true")
+                .setParameter(1, group).setHint("org.hibernate.cacheable", Boolean.TRUE).getResultList();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/merge/FeedsServicePostsIterator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/merge/FeedsServicePostsIterator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/merge/FeedsServicePostsIterator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,73 @@
+package org.jboss.blog.session.merge;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.service.FeedsService;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FeedsServicePostsIterator implements PostsIterator<RestrictedPost> {
+    private FeedsService feedsService;
+    private Feed feed;
+
+    private int chunkSize;
+    private int nextFrom;
+    private RestrictedPost current;
+
+    private Iterator<? extends RestrictedPost> iterator;
+    private int lastChunkSize;
+
+    public FeedsServicePostsIterator(FeedsService feedsService, Feed feed, int chunkSize) {
+        this.feedsService = feedsService;
+        this.feed = feed;
+        this.chunkSize = chunkSize;
+
+        nextFrom = 0;
+
+        readNextChunk();
+
+        if (iterator.hasNext()) {
+            current = iterator.next();
+        } else {
+            current = null;
+        }
+    }
+
+    private void readNextChunk() {
+        List<? extends RestrictedPost> posts = feedsService.getPosts(feed, nextFrom, nextFrom+chunkSize);
+        iterator = posts.iterator();
+        lastChunkSize = posts.size();
+        nextFrom += chunkSize;
+    }
+
+    public RestrictedPost getCurrent() {
+        return current;
+    }
+
+    public boolean finished() {
+        return current == null;
+    }
+
+    public RestrictedPost next() {
+        if (iterator.hasNext()) {
+            current = iterator.next();
+        } else {
+            if (lastChunkSize < chunkSize) {
+                current = null;
+            } else {
+                readNextChunk();
+                if (iterator.hasNext()) {
+                    current = iterator.next();
+                } else {
+                    current = null;
+                }
+            }
+        }
+
+        return current;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/merge/ListPostsIterator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/merge/ListPostsIterator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/merge/ListPostsIterator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,42 @@
+package org.jboss.blog.session.merge;
+
+import org.jboss.blog.model.RestrictedPost;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ListPostsIterator<T extends RestrictedPost> implements PostsIterator<T> {
+    private Iterator<? extends T> iterator;
+    private T current;
+
+    public ListPostsIterator(List<? extends T> posts) {
+        iterator = posts.iterator();
+
+        if (iterator.hasNext()) {
+            current = iterator.next();
+        } else {
+            current = null;
+        }
+    }
+
+    public T getCurrent() {
+        return current;
+    }
+
+    public boolean finished() {
+        return current == null;
+    }
+
+    public T next() {
+        if (iterator.hasNext()) {
+            current = iterator.next();
+        } else {
+            current = null;
+        }
+
+        return current;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/merge/MergeServiceBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/merge/MergeServiceBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/merge/MergeServiceBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,202 @@
+package org.jboss.blog.session.merge;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Enclosure;
+import org.jboss.blog.model.Image;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.PostNotFoundException;
+import org.jboss.blog.service.LinkService;
+import org.jboss.blog.tools.GeneralTools;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.core.Events;
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.log.Log;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("mergeService")
+ at AutoCreate
+ at Scope(ScopeType.STATELESS)
+public class MergeServiceBean {
+    @In
+    private FeedsService feedsService;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private TitleAsIdService titleAsIdService;
+
+    @In
+    private LinkService linkService;
+
+    @Logger
+    private Log log;
+
+    @In
+    private Events events;
+
+    public void savePost(Feed feed, Post post) {
+        // Preparing the post
+        post.setTitleAsId(titleAsIdService.generateTitleAsId(post.getTitle()));
+
+        if (StringTools.isEmpty(post.getLink())) {
+            post.setLink(linkService.generatePostLink(post));
+        }
+
+        post.setContent(StringTools.fixHtml(post.getContent()));
+
+        post.setFeed(feed);
+
+        log.debug("Saving post, feed: #0, post title: #1, post titleAsId: #2, published: #3.",
+                feed.getName(), post.getTitle(), post.getTitleAsId(), post.getPublished());
+
+        // Persisting
+        entityManager.persist(post);
+
+        for (Enclosure enc : post.getEnclosures()) {
+            entityManager.persist(enc);
+        }
+
+        for (Image image : post.getImages()) {
+            entityManager.persist(image);
+        }
+
+        entityManager.flush();
+
+        events.raiseEvent("org.jboss.blog.post.added", post);
+    }
+
+    private boolean checkNewPost(Post post, PostsIterator<RestrictedPost> mergeTo, PostsIterator<Post> mergeFrom) {
+        int currentPostSize = entityManager
+                .createQuery("select p from Post p where p.published = :p and p.title = :t")
+                .setParameter("p", post.getPublished())
+                .setParameter("t", post.getTitle())
+                .getResultList().size();
+
+        if (currentPostSize > 0) {
+            log.warn("Trying to save a duplicate post, merge to: #1, merge from: #2!", mergeTo.getCurrent(),
+                    mergeFrom.getCurrent());
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Merges the given posts; checks for changes in:
+     * - author
+     * - content
+     * - link
+     * - dateModified
+     * - title
+     *
+     * The published date is assumed to be the same.
+     *
+     * Doesn't check for changes in:
+     * - categories.
+     *
+     * Also, in case of a title change, the titleAsId is not changed.
+     *
+     * @param mergeTo Post to which to merge the changes.
+     * @param mergeFrom Post from which to merge the changes.
+     */
+    public void mergePosts(Post mergeTo, Post mergeFrom) {
+        boolean changes = false;
+
+        if (!GeneralTools.objectsEqual(mergeTo.getAuthor(), mergeFrom.getAuthor())) {
+            log.debug("Post '#0' merge, changed author, new: '#1', old: '#2'.",
+                    mergeTo.getTitleAsId(), mergeFrom.getAuthor(), mergeTo.getAuthor());
+            mergeTo.setAuthor(mergeFrom.getAuthor());
+            changes = true;
+        }
+
+        mergeFrom.setContent(StringTools.fixHtml(mergeFrom.getContent()));
+        if (!GeneralTools.objectsEqual(mergeTo.getContent(), mergeFrom.getContent())) {
+            log.debug("Post '#0' merge, changed content, new: '#1', old: '#2'.",
+                    mergeTo.getTitleAsId(), mergeFrom.getContent(), mergeTo.getContent());
+            mergeTo.setContent(mergeFrom.getContent());
+            changes = true;
+        }
+
+        if ((mergeFrom.getLink() != null) && (!GeneralTools.objectsEqual(mergeTo.getLink(), mergeFrom.getLink()))) {
+            log.debug("Post '#0' merge, changed link, new: '#1', old: '#2'.",
+                    mergeTo.getTitleAsId(), mergeFrom.getLink(), mergeTo.getLink());
+            mergeTo.setLink(mergeFrom.getLink());
+            changes = true;
+        }
+
+        if (mergeTo.getModified().getTime()/1000 != mergeFrom.getModified().getTime()/1000) {
+            log.debug("Post '#0' merge, changed modified date, new: '#1', old: '#2'.",
+                    mergeTo.getTitleAsId(), mergeFrom.getModified().getTime(), mergeTo.getModified().getTime());
+            mergeTo.setModified(mergeFrom.getModified());
+            changes = true;
+        }
+
+        if (!GeneralTools.objectsEqual(mergeTo.getTitle(), mergeFrom.getTitle())) {
+            log.debug("Post '#0' merge, changed title, new: '#1', old: '#2'.",
+                    mergeTo.getTitleAsId(), mergeFrom.getTitle(), mergeTo.getTitle());
+            mergeTo.setTitle(mergeFrom.getTitle());
+            changes = true;
+        }
+
+        if (changes) {
+            log.debug("Saving merged changes in post #0.", mergeTo.getTitleAsId());
+
+            entityManager.flush();
+
+            events.raiseEvent("org.jboss.blog.post.updated", mergeTo);
+        }
+    }
+
+    /**
+     *
+     * @param feed Feed, into which <code>posts</code> should be merged.
+     * @param posts A <b>sorted</b> list of posts, that should be merged to <code>feed</code>.
+     */
+    public void merge(Feed feed, List<Post> posts) {
+        PostsIterator<Post> mergeFrom = new ListPostsIterator<Post>(posts);
+        PostsIterator<RestrictedPost> mergeTo = new FeedsServicePostsIterator(feedsService, feed, posts.size()+1);
+
+        while (!mergeFrom.finished()) {
+            if (mergeTo.finished()) {
+                // no more current posts
+                if (checkNewPost(mergeFrom.getCurrent(), mergeTo, mergeFrom)) {
+                    savePost(feed, mergeFrom.getCurrent());
+                }
+                mergeFrom.next();
+            } else {
+                int compare = mergeTo.getCurrent().compareTo(mergeFrom.getCurrent());
+                
+                if (compare > 0) {
+                    // mergeTo post is before mergeFrom post
+                    if (checkNewPost(mergeFrom.getCurrent(), mergeTo, mergeFrom)) {
+                        savePost(feed, mergeFrom.getCurrent());
+                    }
+                    mergeFrom.next();
+                } else if (compare == 0) {
+                    try {
+                        mergePosts(feedsService.getPost(mergeTo.getCurrent().getTitleAsId()), mergeFrom.getCurrent());
+                        mergeTo.next();
+                        mergeFrom.next();
+                    } catch (PostNotFoundException e) {
+                        mergeTo.next();
+                    }
+                } else {
+                    // mergeTo post is after mergeFrom post
+                    // proceeding to the next mergeTo post (if exists)
+                    mergeTo.next();
+                }
+            }
+        }
+
+        entityManager.flush();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/merge/PostManager.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/merge/PostManager.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/merge/PostManager.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,40 @@
+package org.jboss.blog.session.merge;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.core.Events;
+import org.jboss.blog.model.Post;
+
+import javax.faces.application.FacesMessage;
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("postManager")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class PostManager {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private Events events;
+
+    public void deletePost(Post post) {
+        events.raiseEvent("org.jboss.blog.post.before_delete", post);
+
+        entityManager.remove(entityManager.merge(post));
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.post.deleted", post.getTitle());
+
+        events.raiseEvent("org.jboss.blog.post.deleted", post);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/merge/PostsIterator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/merge/PostsIterator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/merge/PostsIterator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,12 @@
+package org.jboss.blog.session.merge;
+
+import org.jboss.blog.model.RestrictedPost;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface PostsIterator<T extends RestrictedPost> {
+    public T getCurrent();
+    public T next();
+    public boolean finished();
+}

Added: feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdService.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdService.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,8 @@
+package org.jboss.blog.session.merge;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface TitleAsIdService {
+    String generateTitleAsId(String title);
+}

Added: feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdServiceBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdServiceBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/merge/TitleAsIdServiceBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,38 @@
+package org.jboss.blog.session.merge;
+
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("titleAsIdService")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class TitleAsIdServiceBean implements TitleAsIdService {
+    @In
+    private EntityManager entityManager;
+
+    public String generateTitleAsId(String title) {
+        String candidate = StringTools.convertTitleToLink(title);
+        int nextCandidateNumber = 0;
+
+        while (true) {
+            int candidateCount = entityManager.createQuery("select post from Post post where post.titleAsId = ?1")
+                    .setParameter(1, candidate).getResultList().size();
+
+            if (candidateCount > 0) {
+                candidate = StringTools.convertTitleToLink(title) + nextCandidateNumber;
+                nextCandidateNumber++;
+            } else {
+                return candidate;
+            }
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/parser/ParserException.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/parser/ParserException.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/parser/ParserException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.session.parser;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ParserException extends Exception {
+    public ParserException() {
+    }
+
+    public ParserException(String message) {
+        super(message);
+    }
+
+    public ParserException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ParserException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/parser/ParserService.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/parser/ParserService.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/parser/ParserService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,15 @@
+package org.jboss.blog.session.parser;
+
+import org.jboss.blog.model.feed.Feed;
+
+import javax.ejb.Local;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Local
+public interface ParserService {
+    void remove();
+
+    Feed parse(String link) throws ParserException;
+}

Added: feeds100P26/src/action/org/jboss/blog/session/parser/ParserServiceImpl.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/parser/ParserServiceImpl.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/parser/ParserServiceImpl.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,163 @@
+package org.jboss.blog.session.parser;
+
+import com.sun.syndication.feed.synd.*;
+import com.sun.syndication.io.FeedException;
+import com.sun.syndication.io.SyndFeedInput;
+import com.sun.syndication.io.XmlReader;
+import org.jboss.blog.model.Category;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Enclosure;
+import org.jboss.blog.model.Image;
+import org.jboss.blog.model.configuration.Configuration;
+import org.jboss.blog.session.category.CategoryServiceBean;
+import org.jboss.blog.session.configuration.ConfigurationManager;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jdom.Element;
+import org.jdom.Attribute;
+import org.jdom.Content;
+
+import javax.ejb.Remove;
+import javax.ejb.Stateless;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Stateless
+ at Name("parserService")
+ at AutoCreate
+public class ParserServiceImpl implements ParserService {
+    @In
+    private CategoryServiceBean categoryService;
+
+    @In
+    private ConfigurationManager configurationManager;
+
+    private Image getImageFromForeignMarkup(Post post, List<Element> foreignMarkup) {
+        for (Element element : foreignMarkup) {
+            if ("image".equals(element.getName())) {
+                Attribute href = element.getAttribute("href");
+                if (href != null) {
+                    return new Image(post, href.getValue());
+                }
+
+                Content content = element.getContent(0);
+                if (content != null) {
+                    return new Image(post, content.getValue());
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public Feed parse(String link) throws ParserException {
+        try {
+            SyndFeedInput input = new SyndFeedInput();
+
+            Configuration conf = configurationManager.getConfiguration();
+
+            URLConnection conn = new URL(link).openConnection();
+            conn.setReadTimeout(conf.getReadTimeout());
+            conn.setConnectTimeout(conf.getConnectionTimeout());
+            conn.connect();
+
+            SyndFeed syndFeed = input.build(new XmlReader(conn.getInputStream()));
+
+            Feed feed = new Feed();
+
+            feed.setAuthor(syndFeed.getAuthor());
+            feed.setDescription(syndFeed.getDescription());
+            feed.setLink(syndFeed.getLink());
+            feed.setTitle(syndFeed.getTitle());
+
+            List<Post> posts = new ArrayList<Post>();
+            feed.setPosts(posts);
+
+            if (syndFeed.getEntries() != null) {
+                for (Object entryObj : syndFeed.getEntries()) {
+                    SyndEntry entry = (SyndEntry) entryObj;
+                    Post post = new Post();
+
+                    post.setFeed(feed);
+
+                    post.setAuthor(StringTools.isEmpty(entry.getAuthor()) ? null : entry.getAuthor());
+
+                    // Setting content
+                    String longestContent = entry.getDescription() == null ? "" : entry.getDescription().getValue();
+
+                    for (Object contentObj : entry.getContents()) {
+                        SyndContent content = (SyndContent) contentObj;
+                        String currentContent = content == null ? "" : content.getValue();
+
+                        if (currentContent.length() > longestContent.length()) {
+                            longestContent = currentContent;
+                        }
+                    }
+
+                    post.setContent(longestContent);
+
+                    // Setting categories
+                    post.setCategories(new ArrayList<Category>());
+                    for (Object categoryObj : entry.getCategories()) {
+                        SyndCategory category = (SyndCategory) categoryObj;
+                        post.getCategories().add(categoryService.getCategory(category.getName()));
+                    }
+
+                    // Setting enclosures
+                    post.setEnclosures(new ArrayList<Enclosure>());
+                    for (Object enclosureObj : entry.getEnclosures()) {
+                        SyndEnclosure enclosure = (SyndEnclosure) enclosureObj;
+                        post.getEnclosures().add(new Enclosure(post, enclosure.getUrl(),
+                                enclosure.getLength(), enclosure.getType()));
+                    }
+
+                    // Setting images
+                    post.setImages(new ArrayList<Image>());
+                    //noinspection unchecked
+                    Image postImage = getImageFromForeignMarkup(post, (List<Element>) entry.getForeignMarkup());
+                    if (postImage != null) {
+                        post.getImages().add(postImage);
+                    }
+
+                    // Setting the published date
+                    Date publishedDate = entry.getPublishedDate();
+                    if (publishedDate == null) {
+                        publishedDate = entry.getUpdatedDate();
+                    }
+
+                    post.setPublished(publishedDate);
+
+                    // And other properties
+                    post.setTitle(entry.getTitle());
+                    post.setModified(
+                            entry.getUpdatedDate() == null ? post.getPublished() : entry.getUpdatedDate());
+                    post.setLink(entry.getLink());
+
+                    posts.add(post);
+                }
+            }
+
+            Collections.sort(posts);
+
+            return feed;
+        } catch (FeedException e) {
+            throw new ParserException(e);
+        } catch (MalformedURLException e) {
+            throw new ParserException(e);
+        } catch (IOException e) {
+            throw new ParserException(e);
+        }
+    }
+
+    @Remove
+    public void remove() { }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/scanner/AnnotationScanner.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/scanner/AnnotationScanner.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/scanner/AnnotationScanner.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,171 @@
+package org.jboss.blog.session.scanner;
+
+import org.jboss.seam.log.Logging;
+import org.jboss.seam.log.Log;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.AnnotationsAttribute;
+
+/**
+ * Almost the same as {@link org.jboss.seam.deployment.Scanner}.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AnnotationScanner {
+    private static final Log log = Logging.getLog(AnnotationScanner.class);
+
+    private ClassLoader classLoader;
+    private Map<Class<? extends Annotation>, ClassHandler> handlers;
+
+    public AnnotationScanner(ClassLoader classLoader, Map<Class<? extends Annotation>, ClassHandler> handlers) {
+        this.classLoader = classLoader;
+        this.handlers = handlers;
+    }
+
+    private void addParentURLsOfResource(Set<String> urlPaths, String resourceName) throws IOException {
+        Enumeration<URL> urlEnum = classLoader.getResources(resourceName);
+        while (urlEnum.hasMoreElements()) {
+            String urlPath = urlEnum.nextElement().getFile();
+            urlPath = URLDecoder.decode(urlPath, "UTF-8");
+
+            if (urlPath.startsWith("file:")) {
+                // On windows urlpath looks like file:/C: on Linux file:/home
+                // substring(5) works for both
+                urlPath = urlPath.substring(5);
+            }
+
+            if (urlPath.indexOf('!') > 0) {
+                urlPath = urlPath.substring(0, urlPath.indexOf('!'));
+            } else {
+                File dirOrArchive = new File(urlPath);
+                if (resourceName != null && resourceName.lastIndexOf('/') > 0) {
+                    dirOrArchive = dirOrArchive.getParentFile();
+                }
+
+                urlPath = dirOrArchive.getParent();
+            }
+
+            String dirOrArchiveName;
+            if (urlPath.lastIndexOf('/') != 0) {
+                dirOrArchiveName = urlPath.substring(urlPath.lastIndexOf('/') + 1);
+            } else {
+                dirOrArchiveName = urlPath;
+            }
+
+            if (dirOrArchiveName.contains("blog")) {
+                urlPaths.add(urlPath);
+            }
+        }
+    }
+
+    private Set<String> getUrlPaths() throws IOException {
+        Set<String> urlPaths = new HashSet<String>();
+
+        addParentURLsOfResource(urlPaths, "seam.properties");
+        addParentURLsOfResource(urlPaths, "META-INF/seam.properties");
+        addParentURLsOfResource(urlPaths, "META-INF/components.xml");
+
+        return urlPaths;
+    }
+
+    public void scan() {
+        Set<String> urlPaths;
+
+        try {
+            urlPaths = getUrlPaths();
+        } catch (IOException e) {
+            log.error("Error obtaining url paths.", e);
+            return;
+        }
+
+        for (String urlPath : urlPaths) {
+            try {
+                File file = new File(urlPath);
+
+                if (file.isDirectory()) {
+                    scanDirectory(file, null);
+                } else {
+                    scanArchive(file);
+                }
+            } catch (IOException e) {
+                log.error("Error while scanning url: " + urlPath, e);
+            }
+        }
+    }
+
+    private void scanArchive(File file) throws IOException {
+        ZipFile zipFile = new ZipFile(file);
+        Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
+
+        while (zipEntries.hasMoreElements()) {
+            ZipEntry zipEntry = zipEntries.nextElement();
+
+            scanItem(zipEntry.getName());
+        }
+    }
+
+    private void scanDirectory(File file, String relativePath) throws IOException {
+        for (File child : file.listFiles()) {
+            String newRealtivePath = relativePath == null ? child.getName() : relativePath + "/" + child.getName();
+
+            if (child.isDirectory()) {
+                scanDirectory(child, newRealtivePath);
+            } else {
+                scanItem(newRealtivePath);
+            }
+        }
+    }
+
+    private ClassFile getClassFile(String name) throws IOException {
+        InputStream stream = classLoader.getResourceAsStream(name);
+        DataInputStream dstream = new DataInputStream(stream);
+
+        try {
+            return new ClassFile(dstream);
+        } finally {
+            dstream.close();
+            stream.close();
+        }
+    }
+
+    private String filenameToClassname(String filename) {
+        return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
+    }
+
+    private void scanItem(String name) throws IOException {        
+        // TODO: remove the second part
+        if (name.endsWith(".class") && name.startsWith("org/jboss/blog/session/feed/dao")) {
+            ClassFile classFile = getClassFile(name);
+
+            AnnotationsAttribute visibleAnnotations = (AnnotationsAttribute)
+                    classFile.getAttribute(AnnotationsAttribute.visibleTag);
+
+            if (visibleAnnotations == null) {
+                return;
+            }
+
+            for (Map.Entry<Class<? extends Annotation>, ClassHandler> entry : handlers.entrySet()) {
+                if (visibleAnnotations.getAnnotation(entry.getKey().getName()) != null) {
+                    try {
+                        entry.getValue().handleClass(classLoader.loadClass(filenameToClassname(name)));
+                    } catch (ClassNotFoundException e) {
+                        log.error("Error while loading class.", e);
+                    }
+                }
+            }
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/scanner/ClassHandler.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/scanner/ClassHandler.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/scanner/ClassHandler.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,8 @@
+package org.jboss.blog.session.scanner;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface ClassHandler {
+    public void handleClass(Class<?> toHandle);
+}

Added: feeds100P26/src/action/org/jboss/blog/session/scanner/Init.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/scanner/Init.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/scanner/Init.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,34 @@
+package org.jboss.blog.session.scanner;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.core.Events;
+import org.jboss.blog.session.feed.type.FeedType;
+import org.jboss.blog.session.feed.type.FeedTypes;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.lang.annotation.Annotation;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("blogInit")
+ at Scope(ScopeType.APPLICATION)
+ at AutoCreate
+public class Init {
+    @In
+    private FeedTypes feedTypes;
+
+    @Observer("org.jboss.seam.postInitialization")
+    public void scanForBlogAnnotations() {
+        Map<Class<? extends Annotation>, ClassHandler> handlers =
+                new HashMap<Class<? extends Annotation>, ClassHandler>();
+        
+        handlers.put(FeedType.class, feedTypes);
+        
+        new AnnotationScanner(Thread.currentThread().getContextClassLoader(), handlers).scan();
+
+        Events.instance().raiseEvent("org.jboss.blog.postBlogInit");                
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/search/PostSearchBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/search/PostSearchBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/search/PostSearchBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,164 @@
+package org.jboss.blog.session.search;
+
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.queryParser.MultiFieldQueryParser;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.search.Query;
+import org.hibernate.search.jpa.FullTextEntityManager;
+import org.hibernate.search.jpa.FullTextQuery;
+import org.jboss.blog.model.Post;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.faces.FacesMessages;
+
+import javax.faces.application.FacesMessage;
+import java.util.ArrayList;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("postSearch")
+ at Scope(ScopeType.CONVERSATION)
+public class PostSearchBean implements Serializable {
+    @In
+    private FullTextEntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    private String query;
+
+    private int from;
+
+    private List<Object[]> results;
+
+    private int resultsCount;
+
+    private static char[] LUCENE_SPECIAL =
+            { '\\', '+', '-', '&', '|', '!', '(', ')', '{', '}', '[', ']', '^', '\"', '~', ':' };
+
+    private static String[] ESCAPED_LUCENE_SPECIAL =
+            { "\\\\", "\\+", "\\-", "\\&", "\\|", "\\!", "\\(", "\\)", "\\{", "\\}", "\\[", "\\]", "\\^", "\\\"", "\\~", "\\:" };
+
+    public String getQuery() {
+        return query;
+    }
+
+    public void setQuery(String query) {
+        this.query = query;
+    }
+
+    private int getNumberOfResultsPerPage() {
+        return 10;
+    }
+
+    public int getFrom() {
+        return from;
+    }
+
+    public void setFrom(int from) {
+        this.from = from;
+    }
+
+    public boolean getShowNext() {
+        return getNextFrom() < resultsCount;
+    }
+
+    public boolean getShowPrevious() {
+        return from != 0;
+    }
+
+    public int getNextFrom() {
+        return from + getNumberOfResultsPerPage();
+    }
+
+    public int getPreviousFrom() {
+        return Math.max(0, from - getNumberOfResultsPerPage());
+    }
+
+    public int getResultsCount() {
+        return resultsCount;
+    }
+
+    public List<Object[]> getResults() {
+        return results;
+    }
+
+    private String replaceAll(String where, char what, String replacement) {
+        StringBuilder sb = new StringBuilder();
+
+        for (int i=0; i<where.length(); i++) {
+            char whereAtI = where.charAt(i);
+            if (whereAtI == what) {
+                sb.append(replacement);
+            } else {
+                sb.append(whereAtI);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    private String escapeQuery(String query) {
+        for (int i=0; i<LUCENE_SPECIAL.length; i++) {
+            query = replaceAll(query, LUCENE_SPECIAL[i], ESCAPED_LUCENE_SPECIAL[i]);
+        }
+        return query;
+    }
+
+    private FullTextQuery getFullTextQuery() throws ParseException {
+        MultiFieldQueryParser parser = new MultiFieldQueryParser(
+                new String[]{ "title", "content", "author" },
+                new StandardAnalyzer());
+        Query luceneQuery = parser.parse(escapeQuery(getQuery()));
+
+        FullTextQuery fullTextQuery = entityManager.createFullTextQuery(luceneQuery, Post.class);
+        fullTextQuery.enableFullTextFilter("unrestrictedFeedFilter");
+        fullTextQuery.setProjection(FullTextQuery.SCORE, FullTextQuery.THIS);
+
+        return fullTextQuery;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void search() {
+        if (query == null || "".equals(query.trim())) {
+            facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.search.emptyquery");
+            results = new ArrayList<Object[]>();
+            resultsCount = 0;
+            return;
+        }
+
+        FullTextQuery fullTextQuery;
+        try {
+            fullTextQuery = getFullTextQuery();
+        } catch (ParseException e) {
+            facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_ERROR, "blog.search.exception",
+                    e.getMessage());
+            results = new ArrayList<Object[]>();
+            resultsCount = 0;
+            return;
+        }
+
+        results = fullTextQuery.setFirstResult(getFrom()).setMaxResults(getNumberOfResultsPerPage())
+                .getResultList();
+        resultsCount = fullTextQuery.getResultSize();
+    }
+
+    public String formatScore(float f) {
+        return String.format("%.2f", f*100);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void reindex() {
+        List<Post> posts = entityManager.createQuery("select post from Post post").getResultList();
+        for (Post post : posts) {
+            entityManager.index(post);
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/search/SearchReindexObserver.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/search/SearchReindexObserver.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/search/SearchReindexObserver.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,30 @@
+package org.jboss.blog.session.search;
+
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.hibernate.search.jpa.FullTextEntityManager;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("searchReindexObserver")
+public class SearchReindexObserver {
+    @In
+    private FullTextEntityManager entityManager;
+
+    @Observer({"org.jboss.blog.feed.updated"})
+    public void feedUpdated(Feed feed) {
+        //noinspection unchecked
+        List<Post> posts = entityManager.createQuery("select post from Post post where post.feed = ?1")
+                .setParameter(1, feed).getResultList();
+
+        for (Post post : posts) {
+            entityManager.index(post);
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/Authenticator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/Authenticator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/Authenticator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,60 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.blog.model.security.SecurityUser;
+import org.jboss.blog.model.security.SecurityGroup;
+import org.jboss.blog.model.security.SecurityMapping;
+import org.jboss.blog.session.security.external.ExternalSecurityService;
+
+import java.util.List;
+
+ at Name("authenticator")
+ at AutoCreate
+public class Authenticator {
+    @In
+    private FeedsIdentity identity;
+
+    @In
+    private ExternalSecurityService externalSecurityService;
+
+    private void addFeedRolesFromMappings(List<SecurityMapping> mappings) {
+        if (mappings != null) {
+            for (SecurityMapping mapping : mappings) {
+                identity.addFeedsRole(mapping.getRole(), mapping.getIdForRole());
+            }
+        }
+    }
+
+    private void flushRoles(SecurityUser user) {
+        identity.setSecurityUser(user);
+        
+        identity.removeAllFeedsRoles();
+
+        if (user != null) {
+            addFeedRolesFromMappings(user.getMappings());
+            for (SecurityGroup securityGroup : externalSecurityService.getGroupsOfUser(user)) {
+                addFeedRolesFromMappings(securityGroup.getMappings());
+            }
+        }
+    }
+
+    public void flushRoles() {
+        flushRoles(externalSecurityService.getUnrestrictedSecurityUser(identity.getSecurityUser()));
+    }
+   
+    public boolean authenticate() {
+        SecurityUser user;
+
+        try {
+            user = externalSecurityService.authenticate(identity.getUsername(), identity.getPassword());
+        } catch (InvalidLoginException e) {
+            return false;
+        }
+
+        flushRoles(user);
+
+        return true;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/FeedsCombinedRole.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/FeedsCombinedRole.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/FeedsCombinedRole.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,43 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.blog.model.security.FeedsSecurityRole;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FeedsCombinedRole {
+    private FeedsSecurityRole role;
+    private Integer id;
+
+    public FeedsCombinedRole(FeedsSecurityRole role, Integer id) {
+        this.role = role;
+        this.id = id;
+    }
+
+    public FeedsSecurityRole getRole() {
+        return role;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof FeedsCombinedRole)) return false;
+
+        FeedsCombinedRole that = (FeedsCombinedRole) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (role != that.role) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (role != null ? role.hashCode() : 0);
+        result = 31 * result + (id != null ? id.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/FeedsIdentity.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/FeedsIdentity.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/FeedsIdentity.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,135 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.seam.security.RuleBasedIdentity;
+import org.jboss.seam.annotations.*;
+import static org.jboss.seam.annotations.Install.APPLICATION;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+import static org.jboss.seam.ScopeType.SESSION;
+import org.jboss.blog.model.security.FeedsSecurityRole;
+import org.jboss.blog.model.security.SecurityUser;
+import org.drools.StatefulSession;
+import org.drools.FactHandle;
+import org.drools.base.ClassObjectFilter;
+
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("org.jboss.seam.security.identity")
+ at Scope(SESSION)
+ at BypassInterceptors
+ at Install(precedence = APPLICATION)
+ at Startup
+ at AutoCreate
+public class FeedsIdentity extends RuleBasedIdentity {
+    private SecurityUser securityUser;
+    private boolean pretendedLogin;
+
+    public SecurityUser getSecurityUser() {
+        return securityUser;
+    }
+
+    public void setSecurityUser(SecurityUser securityUser) {
+        this.securityUser = securityUser;
+    }
+
+    public boolean addFeedsRole(FeedsSecurityRole role) {
+        return addFeedsRole(role, null);
+    }
+
+    public boolean addFeedsRole(FeedsSecurityRole role, Integer id) {
+        StatefulSession securityContext = getSecurityContext();
+
+        if (securityContext != null) {
+            securityContext.insert(new FeedsCombinedRole(role, id));
+            securityContext.fireAllRules();
+            return true;
+        }
+
+        return false;
+    }
+
+    public void removeAllFeedsRoles() {
+        StatefulSession securityContext = getSecurityContext();
+
+        if (securityContext != null) {
+            //noinspection unchecked
+            Iterator<FeedsCombinedRole> iter = securityContext.iterateObjects(
+                    new ClassObjectFilter(FeedsCombinedRole.class));
+            while (iter.hasNext()) {
+                FeedsCombinedRole r = iter.next();
+                FactHandle fh = getSecurityContext().getFactHandle(r);
+                getSecurityContext().retract(fh);
+            }
+
+            securityContext.fireAllRules();
+        }
+    }
+
+    public void removeFeedsRole(FeedsSecurityRole role) {
+        removeFeedsRole(role, null);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void removeFeedsRole(FeedsSecurityRole role, Integer id) {
+        StatefulSession securityContext = getSecurityContext();
+
+        FeedsCombinedRole fcr = new FeedsCombinedRole(role, id);
+
+        if (securityContext != null) {
+            Iterator<FeedsCombinedRole> iter = securityContext.iterateObjects(
+                    new ClassObjectFilter(FeedsCombinedRole.class));
+            while (iter.hasNext()) {
+                FeedsCombinedRole r = iter.next();
+                if (r.equals(fcr)) {
+                    FactHandle fh = getSecurityContext().getFactHandle(r);
+                    getSecurityContext().retract(fh);
+                    break;
+                }
+            }
+
+            securityContext.fireAllRules();
+        }
+    }
+
+    public boolean hasPermission(String s, String s1, Object... objects) {
+        int arraySize = 0;
+        for (Object object : objects) {
+            if (object != null) {
+                arraySize++;
+            }
+        }
+
+        Object[] newObjects = new Object[arraySize];
+        int arrayIndex = 0;
+        for (Object object : objects) {
+            if (object != null) {
+                newObjects[arrayIndex++] = object;
+            }
+        }
+
+        return super.hasPermission(s, s1, newObjects);
+    }
+
+    public boolean isLoggedIn() {
+        if (pretendedLogin) {
+            return true;
+        } else {
+            return super.isLoggedIn();
+        }
+    }
+
+    public void loginAsAdmin() {
+        preAuthenticate();
+        pretendedLogin = true;
+        postAuthenticate();
+
+        addFeedsRole(FeedsSecurityRole.ADMIN);
+    }
+
+    public void loginAs(SecurityUser su) {
+        setSecurityUser(su);
+        pretendedLogin = true;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/InvalidLoginException.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/InvalidLoginException.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/InvalidLoginException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,7 @@
+package org.jboss.blog.session.security;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class InvalidLoginException extends Exception {
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/RestrictedKeyGenerator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/RestrictedKeyGenerator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/RestrictedKeyGenerator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,49 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+
+import javax.persistence.EntityManager;
+import java.util.Random;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("restrictedKeyGenerator")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class RestrictedKeyGenerator {
+    @In
+    private EntityManager entityManager;
+
+    private boolean keyValid(String key) {
+        if (key.length() < 8) {
+            return false;
+        }
+        
+        List results = entityManager.createQuery("select su from SecurityUser su where su.restrictedKey = ?1")
+                .setParameter(1, key).getResultList();
+
+        if (results.size() == 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public String generate() {
+        Random random = new Random();
+
+        String key;
+        do {
+            key = Long.toString(Math.abs(random.nextLong()), 36);
+        } while (!keyValid(key));
+
+        return key;
+    }
+}
+

Added: feeds100P26/src/action/org/jboss/blog/session/security/SecurityGroupConverter.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/SecurityGroupConverter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/SecurityGroupConverter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,28 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.blog.model.security.RestrictedSecurityGroup;
+import org.jboss.blog.model.security.SecurityGroup;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.faces.Converter;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityGroupConverter")
+ at BypassInterceptors
+ at Converter
+public class SecurityGroupConverter implements javax.faces.convert.Converter {
+    @Transactional
+    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
+        return new SecurityGroup(null, value);
+    }
+
+    public String getAsString(FacesContext context, UIComponent cmp, Object value) {
+        return ((RestrictedSecurityGroup) value).getExternalId();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/SecurityModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/SecurityModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/SecurityModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,218 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.blog.model.security.*;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.session.security.external.ExternalSecurityService;
+
+import javax.faces.application.FacesMessage;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityMod")
+ at Scope(ScopeType.CONVERSATION)
+ at AutoCreate
+public class SecurityModBean implements Serializable {
+    @In
+    private ExternalSecurityService externalSecurityService;
+
+    @In
+    private FacesMessages facesMessages;
+
+    private RestrictedSecurityGroup restrictedSecurityGroup;
+    private RestrictedSecurityUser restrictedSecurityUser;
+    private Group group;
+    private Feed feed;
+    private FeedsSecurityRole role;
+
+    public RestrictedSecurityGroup getRestrictedSecurityGroup() {
+        return restrictedSecurityGroup;
+    }
+
+    public void setRestrictedSecurityGroup(RestrictedSecurityGroup restrictedSecurityGroup) {
+        this.restrictedSecurityGroup = restrictedSecurityGroup;
+    }
+
+    public RestrictedSecurityUser getRestrictedSecurityUser() {
+        return restrictedSecurityUser;
+    }
+
+    public void setRestrictedSecurityUser(RestrictedSecurityUser restrictedSecurityUser) {
+        this.restrictedSecurityUser = restrictedSecurityUser;
+    }
+
+    public Group getGroup() {
+        return group;
+    }
+
+    public void setGroup(Group group) {
+        this.group = group;
+    }
+
+    public Feed getFeed() {
+        return feed;
+    }
+
+    public void setFeed(Feed feed) {
+        this.feed = feed;
+    }
+
+    public FeedsSecurityRole getRole() {
+        return role;
+    }
+
+    public void setRole(FeedsSecurityRole role) {
+        this.role = role;
+    }
+
+    public List<SecurityGroup> getAdministratorGroups() {
+        return externalSecurityService.getMapping(FeedsSecurityRole.ADMIN, null).getGroups();
+    }
+
+    public List<SecurityGroup> getGroupAdministratorGroups(Group group) {
+        return externalSecurityService.getMapping(FeedsSecurityRole.GROUP_ADMIN, group.getId()).getGroups();
+    }
+
+    public List<SecurityGroup> getFeedAdministratorGroups(Feed feed) {
+        return externalSecurityService.getMapping(FeedsSecurityRole.FEED_ADMIN, feed.getId()).getGroups();
+    }
+
+    public List<SecurityGroup> getFeedViewersGroups(Feed feed) {
+        return externalSecurityService.getMapping(FeedsSecurityRole.VIEW, feed.getId()).getGroups();
+    }
+
+    public List<SecurityUser> getAdministratorUsers() {
+        return externalSecurityService.getMapping(FeedsSecurityRole.ADMIN, null).getUsers();
+    }
+
+    public List<SecurityUser> getGroupAdministratorUsers(Group group) {
+        return externalSecurityService.getMapping(FeedsSecurityRole.GROUP_ADMIN, group.getId()).getUsers();
+    }
+
+    public List<SecurityUser> getFeedAdministratorUsers(Feed feed) {
+        return externalSecurityService.getMapping(FeedsSecurityRole.FEED_ADMIN, feed.getId()).getUsers();
+    }
+
+    public List<SecurityUser> getFeedViewersUsers(Feed feed) {
+        return externalSecurityService.getMapping(FeedsSecurityRole.VIEW, feed.getId()).getUsers();
+    }
+
+    private SecurityMapping getMapping() {
+        Integer idForRole;
+
+        switch (getRole()) {
+            case GROUP_ADMIN: idForRole = getGroup().getId(); break;
+            case FEED_ADMIN: idForRole = getFeed().getId(); break;
+            case VIEW: idForRole = getFeed().getId(); break;
+            default: idForRole = null;
+        }
+
+        return externalSecurityService.getMapping(getRole(), idForRole);
+    }
+
+    public void addSecurityGroupAsSuperUser() {
+        SecurityGroup sg = externalSecurityService.getUnrestrictedSecurityGroup(getRestrictedSecurityGroup());
+        SecurityMapping mapping = getMapping();
+
+        if (!mapping.getGroups().contains(sg)) {
+            mapping.getGroups().add(sg);
+        }
+
+        String bundleId;
+        Object param = null;
+        switch (getRole()) {
+            case ADMIN: bundleId = "blog.security.group.admin.added"; break;
+            case GROUP_ADMIN: bundleId = "blog.security.group.group.added"; param = getGroup().getDisplayName(); break;
+            case FEED_ADMIN: bundleId = "blog.security.group.feed.added"; param = getFeed().getName(); break;
+            case VIEW: bundleId = "blog.security.group.feedview.added"; param = getFeed().getName(); break;
+            default: return;
+        }
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, bundleId,
+                externalSecurityService.getDisplayName(sg), param);
+    }
+
+    @Restrict("#{identity.hasPermission('security_group', 'add', securityMod.role, securityMod.group, securityMod.feed)}")
+    public void addSecurityGroup() {
+        addSecurityGroupAsSuperUser();
+    }
+
+    @Restrict("#{identity.hasPermission('security_group', 'delete', securityMod.role, securityMod.group, securityMod.feed)}")
+    public void deleteSecurityGroup() {
+        SecurityGroup sg = externalSecurityService.getUnrestrictedSecurityGroup(getRestrictedSecurityGroup());
+        SecurityMapping mapping = getMapping();
+
+        mapping.getGroups().remove(sg);
+
+        String bundleId;
+        Object param = null;
+        switch (getRole()) {
+            case ADMIN: bundleId = "blog.security.group.admin.deleted"; break;
+            case GROUP_ADMIN: bundleId = "blog.security.group.group.deleted"; param = getGroup().getDisplayName(); break;
+            case FEED_ADMIN: bundleId = "blog.security.group.feed.deleted"; param = getFeed().getName(); break;
+            case VIEW: bundleId = "blog.security.group.feedview.deleted"; param = getFeed().getName(); break;
+            default: return;
+        }
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, bundleId,
+                externalSecurityService.getDisplayName(sg), param);
+    }
+
+    public void addSecurityUserAsSuperUser() {
+        SecurityUser su = externalSecurityService.getUnrestrictedSecurityUser(getRestrictedSecurityUser());
+        SecurityMapping mapping = getMapping();
+
+        if (!mapping.getUsers().contains(su)) {
+            mapping.getUsers().add(su);
+        }
+
+        String bundleId;
+        Object param = null;
+        switch (getRole()) {
+            case ADMIN: bundleId = "blog.security.user.admin.added"; break;
+            case GROUP_ADMIN: bundleId = "blog.security.user.group.added"; param = getGroup().getDisplayName(); break;
+            case FEED_ADMIN: bundleId = "blog.security.user.feed.added"; param = getFeed().getName(); break;
+            case VIEW: bundleId = "blog.security.user.feedview.added"; param = getFeed().getName(); break;
+            default: return;
+        }
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, bundleId,
+                externalSecurityService.getDisplayName(su), param);
+    }
+
+    @Restrict("#{identity.hasPermission('security_user', 'add', securityMod.role, securityMod.group, securityMod.feed)}")
+    public void addSecurityUser() {
+        addSecurityUserAsSuperUser();
+    }
+
+    @Restrict("#{identity.hasPermission('security_user', 'delete', securityMod.role, securityMod.group, securityMod.feed)}")
+    public void deleteSecurityUser() {
+        SecurityUser su = externalSecurityService.getUnrestrictedSecurityUser(getRestrictedSecurityUser());
+        SecurityMapping mapping = getMapping();
+
+        mapping.getUsers().remove(su);
+
+        String bundleId;
+        Object param = null;
+        switch (getRole()) {
+            case ADMIN: bundleId = "blog.security.user.admin.deleted"; break;
+            case GROUP_ADMIN: bundleId = "blog.security.user.group.deleted"; param = getGroup().getDisplayName(); break;
+            case FEED_ADMIN: bundleId = "blog.security.user.feed.deleted"; param = getFeed().getName(); break;
+            case VIEW: bundleId = "blog.security.user.feedview.deleted"; param = getFeed().getName(); break;
+            default: return;
+        }
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, bundleId,
+                externalSecurityService.getDisplayName(su), param);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/SecurityObserver.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/SecurityObserver.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/SecurityObserver.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,84 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.In;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.security.FeedsSecurityRole;
+import org.jboss.blog.model.security.SecurityMapping;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityObserver")
+public class SecurityObserver {
+    @In
+    private FeedsIdentity identity;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private SecurityModBean securityMod;
+
+    @In
+    private Authenticator authenticator;
+
+    @Observer({"org.jboss.blog.group.updated"})
+    public void groupUpdated(Group group) { }
+
+    @Observer({"org.jboss.blog.group.added"})
+    public void groupAdded(Group group) {
+        if (!identity.hasPermission("group", "edit", group)) {
+            securityMod.setRole(FeedsSecurityRole.GROUP_ADMIN);
+            securityMod.setGroup(group);
+            securityMod.setRestrictedSecurityUser(identity.getSecurityUser());
+
+            securityMod.addSecurityUserAsSuperUser();
+
+            entityManager.flush();
+
+            authenticator.flushRoles();
+        }
+    }
+
+    @Observer({"org.jboss.blog.group.deleted"})
+    public void groupDeleted(Group group) {
+        //noinspection unchecked
+        List<SecurityMapping> mappings =
+                entityManager.createQuery("select sm from SecurityMapping sm where sm.idForRole = ?1 and sm.role = ?2")
+                        .setParameter(1, group.getId()).setParameter(2, FeedsSecurityRole.GROUP_ADMIN)
+                        .getResultList();
+
+        for (SecurityMapping mapping : mappings) {
+            entityManager.remove(mapping);
+        }
+
+        entityManager.flush();
+    }
+
+    @Observer({"org.jboss.blog.feed.updated"})
+    public void feedUpdated(Feed feed) { }
+
+    @Observer({"org.jboss.blog.feed.added"})
+    public void feedAdded(Feed feed) { }
+
+    @Observer({"org.jboss.blog.feed.deleted"})
+    public void feedDeleted(Feed feed) {
+        //noinspection unchecked
+        List<SecurityMapping> mappings =
+                entityManager.createQuery("select sm from SecurityMapping sm where sm.idForRole = ?1 and sm.role = ?2")
+                        .setParameter(1, feed.getId()).setParameter(2, FeedsSecurityRole.FEED_ADMIN)
+                        .getResultList();
+
+        for (SecurityMapping mapping : mappings) {
+            entityManager.remove(mapping);
+        }
+
+        entityManager.flush();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/SecurityRoleConverter.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/SecurityRoleConverter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/SecurityRoleConverter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,27 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.blog.model.security.FeedsSecurityRole;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.faces.Converter;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityRoleConverter")
+ at BypassInterceptors
+ at Converter
+public class SecurityRoleConverter implements javax.faces.convert.Converter {
+    @Transactional
+    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
+        return FeedsSecurityRole.valueOf(value);
+    }
+
+    public String getAsString(FacesContext context, UIComponent cmp, Object value) {
+        return value.toString();
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserConverter.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserConverter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserConverter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,28 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.blog.model.security.SecurityUser;
+import org.jboss.blog.model.security.RestrictedSecurityUser;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.faces.Converter;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityUserConverter")
+ at BypassInterceptors
+ at Converter
+public class SecurityUserConverter implements javax.faces.convert.Converter {
+    @Transactional
+    public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
+        return new SecurityUser(null, value);
+    }
+
+    public String getAsString(FacesContext context, UIComponent cmp, Object value) {
+        return ((RestrictedSecurityUser) value).getExternalId();
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserKeys.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserKeys.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserKeys.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,62 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.blog.model.security.SecurityUser;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityUserKeys")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class SecurityUserKeys {
+    @In
+    private FeedsIdentity identity;
+
+    @In
+    private RestrictedKeyGenerator restrictedKeyGenerator;
+
+    @In
+    private EntityManager entityManager;
+
+    public void generateKeyForCurrentUser() {
+        SecurityUser su = identity.getSecurityUser();
+        su = entityManager.merge(su);
+
+        su.setRestrictedKey(restrictedKeyGenerator.generate());        
+        identity.setSecurityUser(su);
+
+        entityManager.flush();
+    }
+
+    public String getKeyOfCurrentUser() {
+        if (identity.getSecurityUser() == null) {
+            return null;
+        }
+
+        if (StringTools.isEmpty(identity.getSecurityUser().getRestrictedKey())) {
+            generateKeyForCurrentUser();
+        }
+
+        return identity.getSecurityUser().getRestrictedKey();
+    }
+
+    public SecurityUser seachForSecurityUserByKey(String key) {
+        try {
+            return (SecurityUser) entityManager
+                    .createQuery("select su from SecurityUser su where su.restrictedKey = ?1")
+                    .setParameter(1, key)
+                    .getSingleResult();
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserSelectBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserSelectBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/SecurityUserSelectBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,85 @@
+package org.jboss.blog.session.security;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.blog.session.security.external.ExternalSecurityService;
+import org.jboss.blog.model.security.RestrictedSecurityUser;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.blog.tools.GeneralTools;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityUserSelect")
+ at AutoCreate
+public class SecurityUserSelectBean {
+    private int from;
+
+    private String filter;
+
+    private List<? extends RestrictedSecurityUser> realUsers;
+    private List<? extends RestrictedSecurityUser> users;
+
+    @In
+    private ExternalSecurityService externalSecurityService;
+
+    public int getFrom() {
+        return from;
+    }
+
+    public void setFrom(int from) {
+        this.from = from;
+    }
+
+    public String getFilter() {
+        return filter;
+    }
+
+    public void setFilter(String filter) {
+        this.filter = filter;
+    }
+
+    public int getNumberOfUsersOnPage() {
+        return 25;
+    }
+
+    private void initRealUsers() {
+        if (realUsers == null) {
+            if (StringTools.isEmpty(filter)) {
+                realUsers = externalSecurityService.getUsers(from, getNumberOfUsersOnPage()+1);
+            } else {
+                realUsers = externalSecurityService.getUsers(filter, from, getNumberOfUsersOnPage()+1);
+            }
+        }
+    }
+
+    public List<? extends RestrictedSecurityUser> getUsers() {
+        if (users == null) {
+            initRealUsers();
+            users = GeneralTools.subList(realUsers, 0, getNumberOfUsersOnPage());
+        }
+
+        return users;
+    }
+
+    public boolean getShowPrevious() {
+        return from != 0;
+    }
+
+    public boolean getShowNext() {
+        initRealUsers();
+        return realUsers.size() == getNumberOfUsersOnPage()+1;
+    }
+
+    public int getPreviousFrom() {
+        return Math.max(0, from - getNumberOfUsersOnPage());
+    }
+
+    public int getNextFrom() {
+        return from + getNumberOfUsersOnPage();
+    }
+
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/external/AbstractExternalSecurityService.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/external/AbstractExternalSecurityService.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/external/AbstractExternalSecurityService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,76 @@
+package org.jboss.blog.session.security.external;
+
+import org.jboss.blog.model.security.*;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractExternalSecurityService implements ExternalSecurityService {
+    protected abstract EntityManager getEntityManager();
+
+    public SecurityGroup getUnrestrictedSecurityGroup(RestrictedSecurityGroup securityGroup) {
+        try {
+            SecurityGroup entitySg = (SecurityGroup) getEntityManager().createQuery(
+                    "select sg from SecurityGroup sg where sg.externalId = ?1")
+                    .setParameter(1, securityGroup.getExternalId()).getSingleResult();
+            entitySg.setRealGroup(securityGroup.getRealGroup());
+
+            return entitySg;
+        } catch (NoResultException e) {
+            SecurityGroup sg = new SecurityGroup();
+            sg.setExternalId(securityGroup.getExternalId());
+            sg.setRealGroup(securityGroup.getRealGroup());
+
+            getEntityManager().persist(sg);
+
+            return sg;
+        }
+    }
+
+    public SecurityUser getUnrestrictedSecurityUser(RestrictedSecurityUser securityUser) {
+        try {
+            SecurityUser entitySu = (SecurityUser) getEntityManager().createQuery(
+                    "select su from SecurityUser su where su.externalId = ?1")
+                    .setParameter(1, securityUser.getExternalId()).getSingleResult();
+            entitySu.setRealUser(securityUser.getRealUser());
+
+            return entitySu;
+        } catch (NoResultException e) {
+            SecurityUser su = new SecurityUser();
+            su.setExternalId(securityUser.getExternalId());
+            su.setRealUser(securityUser.getRealUser());
+
+            getEntityManager().persist(su);
+
+            return su;
+        }
+    }
+
+    public SecurityMapping getMapping(FeedsSecurityRole role, Integer idForRole) {
+        try {
+            if (idForRole == null) {
+                return (SecurityMapping) getEntityManager().createQuery(
+                    "select mapping from SecurityMapping mapping where mapping.role = ?1")
+                    .setParameter(1, role).getSingleResult();
+            } else {
+                return (SecurityMapping) getEntityManager().createQuery(
+                    "select mapping from SecurityMapping mapping where mapping.role = ?1 and mapping.idForRole = ?2")
+                    .setParameter(1, role).setParameter(2, idForRole).getSingleResult();
+            }
+        } catch (NoResultException e) {
+            SecurityMapping mapping = new SecurityMapping();
+            mapping.setRole(role);
+            mapping.setIdForRole(idForRole);
+            mapping.setUsers(new ArrayList<SecurityUser>());
+            mapping.setGroups(new ArrayList<SecurityGroup>());
+            getEntityManager().persist(mapping);
+            getEntityManager().flush();
+
+            return mapping;
+        }
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/security/external/DummyExternalSecurityService.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/external/DummyExternalSecurityService.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/external/DummyExternalSecurityService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,98 @@
+package org.jboss.blog.session.security.external;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.security.SecurityUser;
+import org.jboss.blog.model.security.SecurityGroup;
+import org.jboss.blog.model.security.RestrictedSecurityGroup;
+import org.jboss.blog.model.security.RestrictedSecurityUser;
+import org.jboss.blog.session.security.InvalidLoginException;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("externalSecurityService")
+ at AutoCreate
+ at Scope(ScopeType.STATELESS)
+ at Install(precedence = 15)
+public class DummyExternalSecurityService extends AbstractExternalSecurityService {
+    @In
+    private EntityManager entityManager;
+
+    protected EntityManager getEntityManager() {
+        return entityManager;
+    }
+
+    public SecurityUser authenticate(String username, String password) throws InvalidLoginException {
+        SecurityUser user = new SecurityUser();
+        user.setExternalId(username);
+
+        return getUnrestrictedSecurityUser(user);
+    }   
+
+    public List<? extends RestrictedSecurityGroup> getAllGroups() {
+        List<RestrictedSecurityGroup> groups = new ArrayList<RestrictedSecurityGroup>();
+        groups.add(new SecurityGroup("group1", "1"));
+        groups.add(new SecurityGroup("group2", "2"));
+        groups.add(new SecurityGroup("group3", "3"));
+
+        return groups;
+    }
+
+    public List<? extends RestrictedSecurityUser> getUsers(int start, int count) {
+        List<RestrictedSecurityUser> users = new ArrayList<RestrictedSecurityUser>();
+        users.add(new SecurityUser("1", "1"));
+        users.add(new SecurityUser("2", "2"));
+        users.add(new SecurityUser("2", "3"));
+
+        return users;
+    }
+
+    public List<? extends RestrictedSecurityUser> getUsers(String filter, int start, int count) {
+        List<RestrictedSecurityUser> users = new ArrayList<RestrictedSecurityUser>();
+        users.add(new SecurityUser("1", "1"));
+        users.add(new SecurityUser("2", "2"));
+        users.add(new SecurityUser("2", "3"));
+
+        return users;
+    }
+
+    public List<SecurityGroup> getGroupsOfUser(SecurityUser securityUser) {
+        List<SecurityGroup> groups = new ArrayList<SecurityGroup>();
+        groups.add(getUnrestrictedSecurityGroup(
+                new SecurityGroup("group" + securityUser.getExternalId(), securityUser.getExternalId())));
+
+        return groups;
+    }
+
+    public List<? extends RestrictedSecurityUser> getUsersInGroup(RestrictedSecurityGroup securityGroup) {
+        List<SecurityUser> users = new ArrayList<SecurityUser>();
+        users.add(new SecurityUser(securityGroup.getExternalId(), securityGroup.getExternalId()));
+
+        return users;
+    }
+
+    public String getEmail(RestrictedSecurityUser securityUserUser) {
+        return "a at a.pl";
+    }
+
+    public String getDisplayName(RestrictedSecurityUser securityUser) {
+        return securityUser.getExternalId();
+    }
+
+    public String getDisplayName(RestrictedSecurityGroup securityGroup) {
+        if (securityGroup.getRealGroup() == null) {
+            securityGroup.setRealGroup("group" + securityGroup.getExternalId());
+        }
+
+        return securityGroup.getRealGroup().toString();
+    }
+
+    public RestrictedSecurityGroup getAdminGroup() {
+        return new SecurityGroup("group1", "1");
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/external/ExternalSecurityService.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/external/ExternalSecurityService.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/external/ExternalSecurityService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,37 @@
+package org.jboss.blog.session.security.external;
+
+import org.jboss.blog.model.security.*;
+import org.jboss.blog.session.security.InvalidLoginException;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface ExternalSecurityService {
+    SecurityUser authenticate(String username, String password) throws InvalidLoginException;
+
+    SecurityGroup getUnrestrictedSecurityGroup(RestrictedSecurityGroup securityGroup);
+
+    SecurityUser getUnrestrictedSecurityUser(RestrictedSecurityUser securityUser);
+
+    List<? extends RestrictedSecurityGroup> getAllGroups();
+
+    List<? extends RestrictedSecurityUser> getUsers(int start, int count);
+
+    List<? extends RestrictedSecurityUser> getUsers(String filter, int start, int count);
+
+    List<SecurityGroup> getGroupsOfUser(SecurityUser securityUser);
+
+    List<? extends RestrictedSecurityUser> getUsersInGroup(RestrictedSecurityGroup securityGroup);
+
+    String getEmail(RestrictedSecurityUser securityUser);
+
+    String getDisplayName(RestrictedSecurityUser securityUser);
+
+    String getDisplayName(RestrictedSecurityGroup securityGroup);
+
+    RestrictedSecurityGroup getAdminGroup();
+
+    SecurityMapping getMapping(FeedsSecurityRole role, Integer idForRole);
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/filtering/FeedsSecurity.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/filtering/FeedsSecurity.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/filtering/FeedsSecurity.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,36 @@
+package org.jboss.blog.session.security.filtering;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.session.security.FeedsIdentity;
+import org.jboss.blog.model.feed.Feed;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("feedsSecurity")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class FeedsSecurity {
+    @In
+    private FeedsIdentity identity;
+
+    public List<Feed> filterViewableFeeds(List<Feed> feeds) {
+        ArrayList<Feed> ret = new ArrayList<Feed>();
+
+        for (Feed feed : feeds) {
+            boolean isFeedRestricted = feed.isFeedRestricted();
+            if (!isFeedRestricted || identity.hasPermission("feed", "view", feed)) {
+                ret.add(feed);
+            }
+        }
+
+        return ret;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/filtering/GroupsSecurity.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/filtering/GroupsSecurity.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/filtering/GroupsSecurity.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,49 @@
+package org.jboss.blog.session.security.filtering;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.session.security.FeedsIdentity;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("groupsSecurity")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class GroupsSecurity {
+    @In
+    private FeedsIdentity identity;
+
+    public List<Group> filterForFeedMod(List<Group> groups) {
+        List<Group> allowedGroups = new ArrayList<Group>();
+
+        for (Group group : groups) {
+            if (identity.hasPermission("feed", "add", group)) {
+                allowedGroups.add(group);
+            }
+        }
+
+        return allowedGroups;
+    }
+
+    public List<Group> filterForFeedMod(Feed feed, List<Group> groups, boolean add) {
+        List<Group> allowedGroups = new ArrayList<Group>();
+
+        for (Group group : groups) {
+            if ((identity.hasPermission("feed", "add", feed, group)) ||
+                    ((!add) && identity.hasPermission("feed", "edit", feed, group))) {
+                allowedGroups.add(group);
+            }
+        }
+
+        return allowedGroups;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/filtering/HighlightsSecurity.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/filtering/HighlightsSecurity.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/filtering/HighlightsSecurity.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,46 @@
+package org.jboss.blog.session.security.filtering;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.feed.HighlightsFeed;
+import org.jboss.blog.session.security.FeedsIdentity;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("highlightsSecurity")
+ at Scope(ScopeType.CONVERSATION)
+ at AutoCreate
+public class HighlightsSecurity implements Serializable {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FeedsIdentity identity;
+
+    private List<HighlightsFeed> feeds;
+
+    private void initFeeds() {
+        feeds = new ArrayList<HighlightsFeed>();
+
+        for (Object feedObj : entityManager.createQuery("select feed from HighlightsFeed feed").getResultList()) {
+            HighlightsFeed feed = (HighlightsFeed) feedObj;
+            if (identity.hasPermission("feed", "edit", feed, feed.getGroup())) {
+                feeds.add(feed);
+            }
+        }
+    }
+
+    public List<HighlightsFeed> getFeeds() {
+        if (feeds == null) {
+            initFeeds();
+        }
+
+        return feeds;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/security/tools/FeedSecurityTools.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/security/tools/FeedSecurityTools.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/security/tools/FeedSecurityTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,23 @@
+package org.jboss.blog.session.security.tools;
+
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.seam.security.Identity;
+import org.jboss.seam.Component;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FeedSecurityTools {
+    public static boolean canViewFeed(RestrictedFeed feed, boolean restricted) {
+        if (!feed.isFeedRestricted()) {
+            return true;
+        }
+
+        if (!restricted) {
+            return false;
+        }
+
+        Identity identity = (Identity) Component.getInstance(Identity.class);
+        return identity.hasPermission("feed", "view", feed);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/tools/AdminBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/tools/AdminBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/tools/AdminBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,121 @@
+package org.jboss.blog.session.tools;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.log.Log;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.model.Template;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.blog.service.GroupsService;
+import org.jboss.blog.session.cache.CacheManager;
+import org.jboss.blog.session.xml.velocity.TemplateServiceBean;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.STATELESS)
+ at Name("adminBean")
+ at AutoCreate
+public class AdminBean {
+    @In
+    private GroupsService groupsService;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private CacheManager cacheManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private TemplateServiceBean templateService;
+
+    @Logger
+    private Log log;
+
+    private void updateFeedTemplates(Template atomTemplate, Template rss2Template, Template rss1Template, Feed feed) {
+        if (feed.getTemplates().get(XmlType.ATOM) == null) { feed.getTemplates().put(XmlType.ATOM, atomTemplate); }
+        if (feed.getTemplates().get(XmlType.RSS2) == null) { feed.getTemplates().put(XmlType.RSS2, rss2Template); }
+        if (feed.getTemplates().get(XmlType.RSS1) == null) { feed.getTemplates().put(XmlType.RSS1, rss1Template); }
+    }
+
+    public void updateMissingTemplates() {
+        Template atomTemplate = templateService.templatesOfType(XmlType.ATOM).get(0);
+        Template rss2Template = templateService.templatesOfType(XmlType.RSS2).get(0);
+        Template rss1Template = templateService.templatesOfType(XmlType.RSS1).get(0);
+
+        for (Group group : groupsService.getAllGroups()) {
+            for (Feed feed : groupsService.unacceptedFeeds(group)) {
+                updateFeedTemplates(atomTemplate, rss2Template, rss1Template, feed);
+            }
+
+            for (Feed feed : groupsService.allAcceptedFeeds(group)) {
+                updateFeedTemplates(atomTemplate, rss2Template, rss1Template, feed);
+            }
+        }
+    }
+
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void fixHtml() {
+        for (Group group : groupsService.getAllGroups()) {
+            for (Feed feed : groupsService.acceptedFeeds(group)) {
+                for (Post post : feed.getPosts()) {
+                    post.setContent(StringTools.fixHtml(post.getContent()));
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings({"unchecked"})
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void removeDuplicates() {
+        List<Object[]> results = (List<Object[]>) entityManager
+                .createQuery("select title, published, count(*) from Post p group by p.title, p.published")
+                .getResultList();
+
+        int removedDuplicates = 0;
+
+        for (Object[] result : results) {
+            if (((Number) result[2]).intValue() > 1) {
+                log.info("Duplicate post with title: " + result[0] + ", published: " + result[1] +
+                    ", count: " + result[2]);
+
+                removedDuplicates += ((Number) result[2]).intValue();
+
+                String titleAsId = StringTools.convertTitleToLink(result[0].toString());
+
+                // Checking that a "first" post exists
+                List firstPostResult = entityManager
+                        .createQuery("select p from Post p where p.titleAsId = :titleAsId")
+                        .setParameter("titleAsId", titleAsId)
+                        .getResultList();
+
+                if (firstPostResult.size() != 1) {
+                    log.info("No first post with titleAsId = " + titleAsId + "!");
+                } else {
+                    entityManager
+                            .createQuery("delete from Post p where p.titleAsId like :titleAsIdLike " +
+                                    "and p.titleAsId != :titleAsId and p.published = :published")
+                            .setParameter("titleAsIdLike", titleAsId + "%")
+                            .setParameter("titleAsId", titleAsId)
+                            .setParameter("published", result[1])
+                            .executeUpdate();
+                }
+            }
+        }
+
+        cacheManager.evictAll();
+
+        facesMessages.add("Removed " + removedDuplicates + " duplicate posts.");
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/tools/CaptchaToolsBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/tools/CaptchaToolsBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/tools/CaptchaToolsBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,18 @@
+package org.jboss.blog.session.tools;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("captchaTools")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class CaptchaToolsBean {
+    public long getId() {
+        return System.currentTimeMillis();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/tools/PostToToolsBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/tools/PostToToolsBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/tools/PostToToolsBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,64 @@
+package org.jboss.blog.session.tools;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Post;
+
+import java.net.URLEncoder;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("postToTools")
+ at Scope(ScopeType.APPLICATION)
+ at AutoCreate
+public class PostToToolsBean {
+    public String encodeTitleForDelicious(Post post) {
+        try {
+            return URLEncoder.encode(post.getTitle(), "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            return post.getTitle();
+        }
+    }
+
+    public String encodeLink(Post post) {
+        String link = post.getLink();
+        int hashPos;
+        if ((hashPos = link.indexOf('#')) != -1) {
+            link = link.substring(0, hashPos);
+        }
+
+        try {
+            return URLEncoder.encode(link, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            return link;
+        }
+    }
+
+    public String encodeTitleForDigg(Post post) {
+        try {
+            String title = post.getTitle();
+            if (title.length() > 75) {
+                title = title.substring(0, 75);
+            }
+            return URLEncoder.encode(title, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            return "";
+        }
+    }
+
+    public String encodeBodyForDigg(Post post) {
+        try {
+            String content = post.getContent();
+            if (content.length() > 350) {
+                content = content.substring(0, 350);
+            }
+            return URLEncoder.encode(content, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            return "";
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/tools/StringToolsBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/tools/StringToolsBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/tools/StringToolsBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,33 @@
+package org.jboss.blog.session.tools;
+
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("stringTools")
+ at Scope(ScopeType.APPLICATION)
+ at AutoCreate
+public class StringToolsBean {
+    private volatile int summaryLength;
+
+    public StringToolsBean() {
+        summaryLength = 500;
+    }
+
+    public int getSummaryLength() {
+        return summaryLength;
+    }
+
+    public void setSummaryLength(int summaryLength) {
+        this.summaryLength = summaryLength;
+    }
+
+    public String createSummary(String s) {
+        return StringTools.createSummary(s, summaryLength);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/update/UpdateException.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/update/UpdateException.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/update/UpdateException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.session.update;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class UpdateException extends Exception {
+    public UpdateException() {
+    }
+
+    public UpdateException(String message) {
+        super(message);
+    }
+
+    public UpdateException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public UpdateException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandler.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandler.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandler.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,82 @@
+package org.jboss.blog.session.update;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.blog.model.Group;
+import org.jboss.blog.service.GroupsService;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.FeedNotFoundException;
+import org.jboss.blog.session.feed.type.FeedTypes;
+import org.jboss.blog.session.security.FeedsIdentity;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.ScopeType;
+
+import javax.ejb.Remove;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("updateHandler")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class UpdateHandler {
+    @In
+    private GroupsService groupsService;
+
+    @In
+    private FeedsService feedsService;
+
+    @In
+    private FeedTypes feedTypes;
+
+    @In
+    private UpdateManager updateManager;
+
+    @In(create = true)
+    private FeedsIdentity identity;
+
+    public List<String> getFeedsToUpdate() {
+        List<String> ret = new ArrayList<String>();
+
+        for (Group group : groupsService.getAllGroups()) {
+            for (Feed feed : groupsService.acceptedFeeds(group)) {
+                ret.add(feed.getName());
+            }
+
+            for (Feed feed : groupsService.restrictedFeeds(group)) {
+                ret.add(feed.getName());
+            }
+        }
+
+        return ret;
+    }
+
+    public void update(String feedName) {
+        try {
+            update(feedsService.getFeed(feedName));
+        } catch (FeedNotFoundException e) {
+            //noinspection ThrowableInstanceNeverThrown
+            updateManager.addFeedUpdateException(feedName, new UpdateException(e));
+        }
+    }
+
+    public void update(RestrictedFeed feed) {
+        identity.loginAsAdmin();
+        try {
+            feedTypes.getFeedDao(feed).update();
+        } catch (UpdateException e) {
+            updateManager.addFeedUpdateException(feed.getName(), e);
+        } catch (Exception e) {
+            //noinspection ThrowableInstanceNeverThrown
+            updateManager.addFeedUpdateException(feed.getName(), new UpdateException(e));
+        }
+    }
+
+    @Remove
+    public void remove() { }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsync.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsync.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsync.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,17 @@
+package org.jboss.blog.session.update;
+
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.seam.annotations.async.Asynchronous;
+
+import javax.ejb.Local;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Local
+public interface UpdateHandlerAsync {
+    @Asynchronous
+    public void update(RestrictedFeed feed);
+
+    public void remove();
+}

Added: feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsyncImpl.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsyncImpl.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/update/UpdateHandlerAsyncImpl.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,27 @@
+package org.jboss.blog.session.update;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.blog.model.feed.RestrictedFeed;
+
+import javax.ejb.Stateless;
+import javax.ejb.Remove;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Stateless
+ at Name("updateHandlerAsync")
+ at AutoCreate
+public class UpdateHandlerAsyncImpl implements UpdateHandlerAsync {
+    @In
+    private UpdateHandler updateHandler;
+
+    public void update(RestrictedFeed feed) {
+        updateHandler.update(feed);
+    }
+
+    @Remove
+    public void remove() { }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/update/UpdateManager.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/update/UpdateManager.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/update/UpdateManager.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,168 @@
+package org.jboss.blog.session.update;
+
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.blog.session.configuration.ConfigurationManager;
+import org.jboss.blog.tools.GeneralTools;
+
+import javax.faces.application.FacesMessage;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.*;
+import java.text.DateFormat;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("updateManager")
+ at Scope(ScopeType.APPLICATION)
+public class UpdateManager {
+    @In
+    private ConfigurationManager configurationManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    private long lastUpdateStart;
+    private long lastUpdateEnd;
+
+    private AtomicBoolean updateInProgress;
+
+    private List<Exception> globalExceptions;
+    private Map<String, List<UpdateException>> feedUpdateExceptions;
+
+    private ScheduledExecutorService executor;
+
+    @Observer("org.jboss.blog.postBlogInit")
+    @Transactional
+    public void register() {
+        globalExceptions = new ArrayList<Exception>();
+        feedUpdateExceptions = new LinkedHashMap<String, List<UpdateException>>();
+
+        createAndStartExcutor();
+    }
+
+    public void restartUpdateThread() {
+        executor.shutdown();
+        createAndStartExcutor();
+    }
+
+    private void createAndStartExcutor() {
+        executor = Executors.newScheduledThreadPool(1);
+        executor.scheduleAtFixedRate(new UpdateThread(), 10, getUpdateInterval(), TimeUnit.SECONDS);
+
+        updateInProgress = new AtomicBoolean(false);
+    }
+
+    public void addFeedUpdateException(String feedName, UpdateException exception) {
+        List<UpdateException> exceptions = feedUpdateExceptions.get(feedName);
+        if (exceptions == null) {
+            exceptions = new ArrayList<UpdateException>();
+            feedUpdateExceptions.put(feedName, exceptions);
+        }
+
+        if (exceptions.size() < 3) {
+            exceptions.add(exception);
+        }
+    }
+
+    public void addGlobalException(Exception exception) {
+        if (globalExceptions.size() < 3) {
+            globalExceptions.add(exception);
+        }
+    }
+
+    public List<UpdateException> getFeedUpdateExceptionsForFeed(String feedName) {
+        return feedUpdateExceptions.get(feedName);
+    }
+
+    public List<String> getFeedUpdateExceptionNames() {
+        return new ArrayList<String>(feedUpdateExceptions.keySet());
+    }
+
+    public void clearFeedsExceptions() {
+        feedUpdateExceptions.clear();
+    }
+
+    public List<Exception> getGlobalExceptions() {
+        return globalExceptions;
+    }
+
+    public void clearGlobalExceptions() {
+        globalExceptions.clear();
+    }
+
+    public long getLastUpdateEnd() {
+        return lastUpdateEnd;
+    }
+
+    public void setLastUpdateEnd(long lastUpdateEnd) {
+        this.lastUpdateEnd = lastUpdateEnd;
+    }
+
+    public String getLastUpdateEndDate() {
+        return DateFormat.getDateTimeInstance().format(getLastUpdateEnd());
+    }
+
+    public long getLastUpdateStart() {
+        return lastUpdateStart;
+    }
+
+    public void setLastUpdateStart(long lastUpdateStart) {
+        this.lastUpdateStart = lastUpdateStart;
+    }
+
+    public String getLastUpdateStartDate() {
+        return DateFormat.getDateTimeInstance().format(getLastUpdateStart());
+    }
+
+    public AtomicBoolean getUpdateInProgress() {
+        return updateInProgress;
+    }
+
+    public String getNow() {
+        return DateFormat.getDateTimeInstance().format(System.currentTimeMillis());
+    }
+
+    public int getUpdateInterval() {
+        return configurationManager.getConfiguration().getUpdateInterval();
+    }
+
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void setUpdateInterval(int updateInterval) {
+        configurationManager.getConfiguration().setUpdateInterval(updateInterval);
+    }
+
+    public int getConnectionTimeout() {
+        return configurationManager.getConfiguration().getConnectionTimeout();
+    }
+
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void setConnectionTimeout(int connectionTimeout) {
+        configurationManager.getConfiguration().setConnectionTimeout(connectionTimeout);
+    }
+
+    public int getReadTimeout() {
+        return configurationManager.getConfiguration().getReadTimeout();
+    }
+
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void setReadTimeout(int readTimeout) {
+        configurationManager.getConfiguration().setReadTimeout(readTimeout);
+    }
+
+    public String getExceptionStackTrace(Exception e) {
+        return GeneralTools.getExceptionStackTrace(e);
+    }
+
+    @Restrict("#{identity.hasPermission('admin', null)}")
+    public void save() {
+        restartUpdateThread();
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.configuration.saved");
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/update/UpdateThread.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/update/UpdateThread.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/update/UpdateThread.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,111 @@
+package org.jboss.blog.session.update;
+
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.contexts.Lifecycle;
+import org.jboss.seam.Component;
+import org.jboss.seam.log.Logging;
+
+import javax.transaction.UserTransaction;
+import javax.transaction.Status;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class UpdateThread implements Runnable {
+    public void run() {
+        List<String> feedsToUpdate = executeInTransaction(new RunnableWithReturn<List<String>>() {
+            public List<String> run() {
+                UpdateManager updateManager = (UpdateManager) Component.getInstance("updateManager");
+
+                if (!updateManager.getUpdateInProgress().getAndSet(true)) {
+                    updateManager.setLastUpdateStart(System.currentTimeMillis());
+
+                    return ((UpdateHandler) Component.getInstance("updateHandler")).getFeedsToUpdate();
+                } else {
+                    //noinspection ThrowableInstanceNeverThrown
+                    updateManager.addGlobalException(new Exception("New update started before the last one finished, " +
+                            "at " + DateFormat.getDateTimeInstance().format(new Date()) + "!"));
+
+                    updateManager.setLastUpdateEnd(System.currentTimeMillis());
+                    updateManager.getUpdateInProgress().set(false);
+                    return null;
+                }
+            }
+        });
+
+        if (feedsToUpdate != null) {
+            for (final String feedName : feedsToUpdate) {
+                executeInTransaction(new RunnableWithReturn<Object>() {
+                    public Object run() {
+                        ((UpdateHandler) Component.getInstance("updateHandler")).update(feedName);
+                        return null;
+                    }
+                });
+            }
+
+            executeInTransaction(new RunnableWithReturn<Object>() {
+                public List<Object> run() {
+                    UpdateManager updateManager = (UpdateManager) Component.getInstance("updateManager");
+
+                    updateManager.setLastUpdateEnd(System.currentTimeMillis());
+                    updateManager.getUpdateInProgress().set(false);
+
+                    return null;
+                }
+            });
+        }
+    }
+
+    private <T> T executeInTransaction(RunnableWithReturn<T> runnable) {
+        try {
+            boolean createContexts = !Contexts.isEventContextActive() && !Contexts.isApplicationContextActive();
+            if (createContexts) {
+                Lifecycle.beginCall();
+            }
+
+            try {
+                UserTransaction tx = null;
+                boolean txStarted = false;
+                try {
+                    tx = (UserTransaction) Component.getInstance("org.jboss.seam.transaction.transaction");
+                    if (tx.getStatus() != Status.STATUS_ACTIVE) {
+                        txStarted = true;
+                        tx.begin();
+                    }
+
+                    T ret = runnable.run();
+
+                    if (txStarted) {
+                        tx.commit();
+                    }
+
+                    return ret;
+                } catch (Throwable e) {
+                    try {
+                        if (txStarted) {
+                            tx.rollback();
+                        }
+                    } catch (Throwable e1) {
+                        Logging.getLog(UpdateManager.class).error("Exception when rolling back the transaction", e1);
+                    }
+
+                    return null;
+                }
+            } finally {
+                if (createContexts) {
+                    Lifecycle.endCall();
+                }
+            }
+        } catch (Throwable t) {
+            Logging.getLog(UpdateManager.class).error("Exception when updating!", t);
+            return null;
+        }
+    }
+
+    private static interface RunnableWithReturn<T> {
+        T run();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/validator/UniqueFeedNameValidator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/validator/UniqueFeedNameValidator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/validator/UniqueFeedNameValidator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,62 @@
+package org.jboss.blog.session.validator;
+
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.faces.Validator;
+import org.jboss.seam.international.Messages;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.validator.ValidatorException;
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Validator
+ at Name("uniqueFeedNameValidator")
+public class UniqueFeedNameValidator implements javax.faces.validator.Validator {
+    @In
+    private EntityManager entityManager;
+
+    private String entityId;
+
+    public String getEntityId() {
+        return entityId;
+    }
+
+    public void setEntityId(String entityId) {
+        this.entityId = entityId;
+    }
+
+    public void validate(FacesContext context, UIComponent cmp, Object value)
+            throws ValidatorException {
+        String name = StringTools.safeToString(value);
+
+        Integer id;
+
+        if (StringTools.isEmpty(entityId)) {
+            id = null;
+        } else {
+            id = Integer.parseInt(entityId);
+        }
+
+        Query query;
+
+        if (id == null) {
+            query = entityManager.createQuery("select feed.id from Feed feed where feed.name = ?1")
+                    .setParameter(1, name);
+        } else {
+            query = entityManager.createQuery(
+                    "select feed.id from Feed feed where feed.name = ?1 and not (feed.id = ?2)")
+                    .setParameter(1, name).setParameter(2, id);
+        }
+
+        if (query.getResultList().size() != 0) {
+            throw new ValidatorException(new FacesMessage(Messages.instance().get("blog.feed.new.existingname")));
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/validator/UniqueGroupNameValidator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/validator/UniqueGroupNameValidator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/validator/UniqueGroupNameValidator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,62 @@
+package org.jboss.blog.session.validator;
+
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.faces.Validator;
+import org.jboss.seam.international.Messages;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.validator.ValidatorException;
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Validator
+ at Name("uniqueGroupNameValidator")
+public class UniqueGroupNameValidator implements javax.faces.validator.Validator {
+    @In
+    private EntityManager entityManager;
+
+    private String entityId;
+
+    public String getEntityId() {
+        return entityId;
+    }
+
+    public void setEntityId(String entityId) {
+        this.entityId = entityId;
+    }
+
+    public void validate(FacesContext context, UIComponent cmp, Object value)
+            throws ValidatorException {
+        String name = StringTools.safeToString(value);
+
+        Integer id;
+
+        if (StringTools.isEmpty(entityId)) {
+            id = null;
+        } else {
+            id = Integer.parseInt(entityId);
+        }
+
+        Query query;
+
+        if (id == null) {
+            query = entityManager.createQuery("select g.id from Group g where g.name = ?1")
+                    .setParameter(1, name);
+        } else {
+            query = entityManager.createQuery(
+                    "select g.id from Group g where g.name = ?1 and not (g.id = ?2)")
+                    .setParameter(1, name).setParameter(2, id);
+        }
+
+        if (query.getResultList().size() != 0) {
+            throw new ValidatorException(new FacesMessage(Messages.instance().get("blog.group.existingname")));
+        }
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/validator/UniqueTemplateNameValidator.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/validator/UniqueTemplateNameValidator.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/validator/UniqueTemplateNameValidator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,62 @@
+package org.jboss.blog.session.validator;
+
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.faces.Validator;
+import org.jboss.seam.international.Messages;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.validator.ValidatorException;
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Validator
+ at Name("uniqueTemplateNameValidator")
+public class UniqueTemplateNameValidator implements javax.faces.validator.Validator {
+    @In
+    private EntityManager entityManager;
+
+    private String entityId;
+
+    public String getEntityId() {
+        return entityId;
+    }
+
+    public void setEntityId(String entityId) {
+        this.entityId = entityId;
+    }
+
+    public void validate(FacesContext context, UIComponent cmp, Object value)
+            throws ValidatorException {
+        String name = StringTools.safeToString(value);
+
+        Integer id;
+
+        if (StringTools.isEmpty(entityId)) {
+            id = null;
+        } else {
+            id = Integer.parseInt(entityId);
+        }
+
+        Query query;
+
+        if (id == null) {
+            query = entityManager.createQuery("select t.id from Template t where t.name = ?1")
+                    .setParameter(1, name);
+        } else {
+            query = entityManager.createQuery(
+                    "select t.id from Template t where t.name = ?1 and not (t.id = ?2)")
+                    .setParameter(1, name).setParameter(2, id);
+        }
+
+        if (query.getResultList().size() != 0) {
+            throw new ValidatorException(new FacesMessage(Messages.instance().get("blog.template.new.existingname")));
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/view/FeedViewBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/view/FeedViewBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/view/FeedViewBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,67 @@
+package org.jboss.blog.session.view;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("feedView")
+ at Scope(ScopeType.CONVERSATION)
+public class FeedViewBean implements Serializable {
+    @In
+    private FeedsService feedsService;
+
+    private Feed feed;
+    private int from;
+
+    private List<? extends RestrictedPost> posts;
+
+    public Feed getFeed() {
+        return feed;
+    }
+
+    public void setFeed(Feed feed) {
+        this.feed = feed;
+    }
+
+    public int getFrom() {
+        return from;
+    }
+
+    public void setFrom(int from) {
+        this.from = from;
+    }
+
+    public List<? extends RestrictedPost> getPosts() {
+        if (posts == null) {
+            posts = feedsService.getPosts(feed, from, from+feed.getMaxPostsOnPage()+1);
+        }
+
+        return posts;
+    }
+
+    public boolean getShowPrevious() {
+        return from != 0;
+    }
+
+    public boolean getShowNext() {
+        return getPosts().size() == feed.getMaxPostsOnPage()+1;
+    }
+
+    public int getPreviousFrom() {
+        return Math.max(0, from - feed.getMaxPostsOnPage());
+    }
+
+    public int getNextFrom() {
+        return from + feed.getMaxPostsOnPage();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/view/LinkServiceImpl.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/view/LinkServiceImpl.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/view/LinkServiceImpl.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,61 @@
+package org.jboss.blog.session.view;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.service.LinkService;
+import org.jboss.blog.session.security.SecurityUserKeys;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.ScopeType;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at AutoCreate
+ at Scope(ScopeType.STATELESS)
+ at Name("linkService")
+public class LinkServiceImpl implements LinkService {
+    @In
+    private SecurityUserKeys securityUserKeys;
+
+    private String serverAddress;
+    private String contextName;
+
+    public String getServerAddress() {
+        return serverAddress;
+    }
+
+    public void setServerAddress(String serverAddress) {
+        this.serverAddress = serverAddress;
+    }
+
+    public String getContextName() {
+        return contextName;
+    }
+
+    public void setContextName(String contextName) {
+        this.contextName = contextName;
+    }
+
+    public String generateFeedLink(Feed feed, XmlType type) {
+        String base = serverAddress + "/" + contextName + "/xml/" + feed.getName()
+                + "?type=" + type.toString().toLowerCase();
+        
+        if (feed.isFeedRestricted()) {
+            return base + "&key=" + securityUserKeys.getKeyOfCurrentUser();
+        } else {
+            return base;
+        }
+    }
+
+    public String generateFeedPageLink(Feed feed) {
+        return serverAddress + "/" + contextName + "/view/" + feed.getName();
+    }
+
+    public String generatePostLink(Post post) {
+        return serverAddress + "/" + contextName + "/post/" + post.getTitleAsId();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/view/PostViewBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/view/PostViewBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/view/PostViewBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,34 @@
+package org.jboss.blog.session.view;
+
+import org.jboss.blog.model.Post;
+import org.jboss.blog.session.merge.PostManager;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.security.Restrict;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("postView")
+public class PostViewBean implements Serializable {
+    @In
+    private PostManager postManager;
+
+    private Post post;
+
+    public Post getPost() {
+        return post;
+    }
+
+    public void setPost(Post post) {
+        this.post = post;
+    }
+
+    // TODO: the delete method shouldn't be here
+    @Restrict("#{identity.hasPermission('post', 'delete', postView.post, postView.post.feed, postView.post.feed.group)}")
+    public void delete() {
+        postManager.deletePost(post);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/XmlService.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/XmlService.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/XmlService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,11 @@
+package org.jboss.blog.session.xml;
+
+import org.jboss.blog.session.xml.content.ContentResponse;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface XmlService {
+    void writeXml(String feedType, String feedName, String feedKey, ContentResponse response)
+            throws Exception;
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/content/ContentResponse.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/content/ContentResponse.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/content/ContentResponse.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,19 @@
+package org.jboss.blog.session.xml.content;
+
+import java.io.Writer;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface ContentResponse {
+    public String getContentType();
+
+    public void setContentType(String contentType);
+
+    public String getCharacterEncoding();
+
+    public void setCharacterEncoding(String characterEncoding);
+
+    public Writer getWriter() throws IOException;
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/content/InMemoryContentResponse.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/content/InMemoryContentResponse.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/content/InMemoryContentResponse.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,37 @@
+package org.jboss.blog.session.xml.content;
+
+import java.io.Writer;
+import java.io.CharArrayWriter;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class InMemoryContentResponse implements ContentResponse {
+    private String contentType;
+    private String characterEncoding;
+    private CharArrayWriter writer;
+
+    public InMemoryContentResponse() {
+        writer = new CharArrayWriter();
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    public String getCharacterEncoding() {
+        return characterEncoding;
+    }
+
+    public void setCharacterEncoding(String characterEncoding) {
+        this.characterEncoding = characterEncoding;
+    }
+
+    public Writer getWriter() {
+        return writer;
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/content/ServletResponseContentResponse.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/content/ServletResponseContentResponse.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/content/ServletResponseContentResponse.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,37 @@
+package org.jboss.blog.session.xml.content;
+
+import javax.servlet.ServletResponse;
+import java.io.Writer;
+import java.io.PrintWriter;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ServletResponseContentResponse implements ContentResponse {
+    private ServletResponse servletResponse;
+
+    public ServletResponseContentResponse(ServletResponse servletResponse) {
+        this.servletResponse = servletResponse;
+    }
+
+    public String getCharacterEncoding() {
+        return servletResponse.getCharacterEncoding();
+    }
+
+    public String getContentType() {
+        return servletResponse.getContentType();
+    }
+
+    public Writer getWriter() throws IOException {
+        return servletResponse.getWriter();
+    }
+
+    public void setCharacterEncoding(String s) {
+        servletResponse.setCharacterEncoding(s);
+    }
+
+    public void setContentType(String s) {
+        servletResponse.setContentType(s);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/DatabaseResourceLoader.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/DatabaseResourceLoader.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/DatabaseResourceLoader.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,50 @@
+package org.jboss.blog.session.xml.velocity;
+
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+import org.jboss.blog.model.Template;
+import org.jboss.seam.Component;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DatabaseResourceLoader extends ResourceLoader {
+    public void init(ExtendedProperties extendedProperties) {
+    }
+
+    private Template getTemplate(String name) {
+        return (Template) ((EntityManager) Component.getInstance("entityManager")).createQuery(
+                "select t from Template t where t.name = ?1").setParameter(1, name).getSingleResult();
+    }
+
+    public InputStream getResourceStream(String name) throws ResourceNotFoundException {
+        try {
+            return new ByteArrayInputStream(getTemplate(name).getText().getBytes());
+        } catch (NoResultException e) {
+            return null;
+        }
+    }
+
+    public boolean isSourceModified(Resource resource) {
+        try {
+            return resource.getLastModified() != getLastModified(resource);
+        } catch (NoResultException e) {
+            return false;
+        }
+    }
+
+    public long getLastModified(Resource resource) {
+        try {
+            return getTemplate(resource.getName()).getLastModified().getTime();
+        } catch (NoResultException e) {
+            return 0;
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/InvalidTemplateTypeException.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/InvalidTemplateTypeException.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/InvalidTemplateTypeException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.session.xml.velocity;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class InvalidTemplateTypeException extends Exception {
+    public InvalidTemplateTypeException() {
+    }
+
+    public InvalidTemplateTypeException(String message) {
+        super(message);
+    }
+
+    public InvalidTemplateTypeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public InvalidTemplateTypeException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateBootstrap.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateBootstrap.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateBootstrap.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,82 @@
+package org.jboss.blog.session.xml.velocity;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Template;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.tools.GeneralTools;
+
+import javax.persistence.EntityManager;
+import java.util.Date;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("templateBootstrap")
+ at Scope(ScopeType.STATELESS)
+public class TemplateBootstrap {
+    @In
+    private EntityManager entityManager;
+
+    private long getTemplateCount(XmlType type) {
+        return (Long) entityManager
+                .createQuery("select count(t) from Template t where t.type = :type")
+                .setParameter("type", type)
+                .getSingleResult();
+    }
+    
+    @Observer("org.jboss.blog.postBlogInit")
+    @Transactional
+    public void initAtomTemplate() throws IOException {
+        if (getTemplateCount(XmlType.ATOM) == 0) {
+            String templateText = GeneralTools.readInputStream(
+                    this.getClass().getResourceAsStream("/templates/atom_standard.vm"));
+            
+            Template atomTemplate = new Template();
+            atomTemplate.setType(XmlType.ATOM);
+            atomTemplate.setLastModified(new Date());
+            atomTemplate.setName("0_atom_standard");
+            atomTemplate.setText(templateText);
+
+            entityManager.persist(atomTemplate);
+            entityManager.flush();
+        }
+    }
+
+    @Observer("org.jboss.blog.postBlogInit")
+    @Transactional
+    public void initRss2Template() throws IOException {
+        if (getTemplateCount(XmlType.RSS2) == 0) {
+            String templateText = GeneralTools.readInputStream(
+                    this.getClass().getResourceAsStream("/templates/rss2_standard.vm"));
+
+            Template rss2Template = new Template();
+            rss2Template.setType(XmlType.RSS2);
+            rss2Template.setLastModified(new Date());
+            rss2Template.setName("0_rss2_standard");
+            rss2Template.setText(templateText);
+
+            entityManager.persist(rss2Template);
+            entityManager.flush();
+        }
+    }
+
+    @Observer("org.jboss.blog.postBlogInit")
+    @Transactional
+    public void initRss1Template() throws IOException {
+        if (getTemplateCount(XmlType.RSS1) == 0) {
+            String templateText = GeneralTools.readInputStream(
+                    this.getClass().getResourceAsStream("/templates/rss1_standard.vm"));
+
+            Template rss1Template = new Template();
+            rss1Template.setType(XmlType.RSS1);
+            rss1Template.setLastModified(new Date());
+            rss1Template.setName("0_rss1_standard");
+            rss1Template.setText(templateText);
+
+            entityManager.persist(rss1Template);
+            entityManager.flush();
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateModBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateModBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,71 @@
+package org.jboss.blog.session.xml.velocity;
+
+import org.jboss.blog.model.Template;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.core.Events;
+
+import javax.persistence.EntityManager;
+import javax.faces.application.FacesMessage;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("templateMod")
+public class TemplateModBean {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private Events events;
+
+    private Template template;
+
+    public Template getTemplate() {
+        if (template == null) {
+            template = new Template();
+        }
+
+        return template;
+    }
+
+    public void setTemplate(Template template) {
+        this.template = template;
+    }
+
+    @Restrict("#{identity.hasPermission('template', 'add')}")
+    public void saveNew() {
+        entityManager.persist(template);
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.template.added", template.getName(),
+                template.getType());
+
+        events.raiseEvent("org.jboss.blog.template.added", template);
+    }
+
+    @Restrict("#{identity.hasPermission('template', 'edit', templateMod.template)}")
+    public void saveExisting() {
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.template.updated", template.getName(),
+                template.getType());
+
+        events.raiseEvent("org.jboss.blog.template.updated", template);
+    }
+
+    @Restrict("#{identity.hasPermission('template', 'delete', templateMod.template)}")
+    public void delete() {
+        entityManager.remove(template);
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.template.deleted", template.getName(),
+                template.getType());
+
+        events.raiseEvent("org.jboss.blog.template.deleted", template);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateServiceBean.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateServiceBean.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/TemplateServiceBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,34 @@
+package org.jboss.blog.session.xml.velocity;
+
+import org.jboss.blog.model.Template;
+import org.jboss.blog.model.XmlType;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("templateService")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class TemplateServiceBean {
+    @In
+    private EntityManager entityManager;
+
+    @SuppressWarnings("unchecked")
+    public List<Template> getAllTemplates() {
+        return entityManager.createQuery("select t from Template t order by t.name, t.type").getResultList();
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<org.jboss.blog.model.Template> templatesOfType(XmlType type) {
+        return entityManager.createQuery("select t from Template t where t.type = ?1 order by t.name")
+                .setParameter(1, type).getResultList();
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/VelocityXmlService.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/VelocityXmlService.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/VelocityXmlService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,147 @@
+package org.jboss.blog.session.xml.velocity;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.FeedNotFoundException;
+import org.jboss.blog.session.xml.XmlService;
+import org.jboss.blog.session.xml.content.ContentResponse;
+import org.jboss.blog.session.xml.content.InMemoryContentResponse;
+import org.jboss.blog.session.xml.velocity.tools.XmlTools;
+import org.jboss.blog.session.xml.velocity.tools.AtomXmlTools;
+import org.jboss.blog.session.xml.velocity.tools.Rss2XmlTools;
+import org.jboss.blog.session.xml.velocity.tools.Rss1XmlTools;
+import org.jboss.blog.session.cache.CacheManager;
+import org.jboss.blog.session.tools.PostToToolsBean;
+import org.jboss.blog.session.security.SecurityUserKeys;
+import org.jboss.blog.session.security.FeedsIdentity;
+import org.jboss.blog.session.security.Authenticator;
+import org.jboss.blog.model.security.SecurityUser;
+import org.jboss.blog.tools.GeneralTools;
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.ScopeType;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("xmlService")
+ at Scope(ScopeType.STATELESS)
+ at AutoCreate
+public class VelocityXmlService implements XmlService {
+    @In
+    private FeedsService feedsService;
+
+    @In
+    private CacheManager cacheManager;
+
+    @In
+    private PostToToolsBean postToTools;
+
+    @In
+    private SecurityUserKeys securityUserKeys;
+
+    @In
+    private Authenticator authenticator;
+
+    @In
+    private FeedsIdentity identity;
+
+    @Logger
+    private Log log;
+
+    private VelocityEngine engine;
+
+    private Map<XmlType, XmlTools> xmlTools;
+
+    @PostConstruct
+    public void initVeloctiy() {
+        xmlTools = new HashMap<XmlType, XmlTools>();
+        xmlTools.put(XmlType.ATOM, new AtomXmlTools());
+        xmlTools.put(XmlType.RSS2, new Rss2XmlTools());
+        xmlTools.put(XmlType.RSS1, new Rss1XmlTools());
+
+        try {
+            Properties props = new Properties();
+            props.load(this.getClass().getResourceAsStream("/velocity.properties"));
+
+            engine = new VelocityEngine(props);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void writeXml(String feedType, String feedName, String feedKey, ContentResponse response)
+            throws Exception {
+        InMemoryContentResponse inCache = cacheManager.getFeedXml(feedName, feedType);
+
+        if (inCache == null) {
+            inCache = new InMemoryContentResponse();
+
+            log.debug("Generating xml for feed '#0' of type: #1.", feedName, feedType);
+
+            XmlType xmlType;
+            try {
+                xmlType = XmlType.valueOf(feedType == null ? "" : feedType.toUpperCase());
+            } catch (IllegalArgumentException e) {
+                throw new InvalidTemplateTypeException(e);
+            }
+
+            Feed feed = feedsService.getFeed(feedName);
+
+            boolean isRestricted = feed.isFeedRestricted();
+            if (isRestricted) {
+                SecurityUser su = securityUserKeys.seachForSecurityUserByKey(feedKey);
+
+                if (su == null) {
+                    throw new FeedNotFoundException();
+                } else {
+                    // "Loggging in" the found user
+                    identity.loginAs(su);
+                    authenticator.flushRoles();
+                }
+            }
+
+            if (feed.getTemplates().get(xmlType) == null) {
+                throw new InvalidTemplateTypeException();
+            }
+
+            List<? extends RestrictedPost> posts = feedsService.getPosts(feed, 0, feed.getMaxPostsInFeed(), true);
+
+            inCache.setContentType(xmlType.contentType());
+            inCache.setCharacterEncoding("utf-8");
+
+            VelocityContext context = new VelocityContext();
+            context.put("posts", posts);
+            context.put("feed", feed);
+            context.put("tools", xmlTools.get(xmlType));
+            context.put("xmlType", xmlType);
+            context.put("postToTools", postToTools);
+
+            Template template = engine.getTemplate(feed.getTemplates().get(xmlType).getName());
+            template.merge(context, inCache.getWriter());
+
+            if (!isRestricted) {
+                cacheManager.putFeedXml(feedName, feedType, inCache);
+            }
+        }
+
+        response.setCharacterEncoding(inCache.getCharacterEncoding());
+        response.setContentType(inCache.getContentType());
+
+        GeneralTools.transfer(new CharArrayReader(((CharArrayWriter) inCache.getWriter()).toCharArray()),
+                response.getWriter());
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/AtomXmlTools.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/AtomXmlTools.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/AtomXmlTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,16 @@
+package org.jboss.blog.session.xml.velocity.tools;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AtomXmlTools extends XmlTools {
+    public String formatDate(Date date) {
+		String noZoneDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(date);
+		String zone = new SimpleDateFormat("Z").format(date);
+
+		return noZoneDate + zone.substring(0, 3) + ":" + zone.substring(3, 5);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss1XmlTools.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss1XmlTools.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss1XmlTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,13 @@
+package org.jboss.blog.session.xml.velocity.tools;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Rss1XmlTools extends XmlTools {
+    public String formatDate(Date date) {
+        return new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z").format(date);
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss2XmlTools.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss2XmlTools.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/Rss2XmlTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,13 @@
+package org.jboss.blog.session.xml.velocity.tools;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Rss2XmlTools extends XmlTools {
+    public String formatDate(Date date) {
+        return new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z").format(date);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/XmlTools.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/XmlTools.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/session/xml/velocity/tools/XmlTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,53 @@
+package org.jboss.blog.session.xml.velocity.tools;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.service.LinkService;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.seam.Component;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class XmlTools {
+    public String formatDate(Date date) {
+        return new SimpleDateFormat("MM/dd/yy").format(date);
+    }
+
+    public Date feedPubDate(Feed feed, List<Post> posts) {
+        if (posts.size() != 0) {
+            return posts.get(0).getPublished();
+        }
+
+        Calendar cal = Calendar.getInstance();
+        cal.setTimeInMillis(0);
+
+        return cal.getTime();
+    }
+
+    public String escape(String toEscape) {
+        return StringTools.escape(toEscape);
+    }
+
+    private LinkService getLinkService() {
+        return (LinkService) Component.getInstance("linkService");
+    }
+
+    public String feedPageLink(Feed feed) {
+        return getLinkService().generateFeedPageLink(feed);
+    }
+
+    public String feedLink(Feed feed, XmlType type) {
+        return getLinkService().generateFeedLink(feed, type);
+    }
+
+    public String postLink(Post post) {
+        return getLinkService().generatePostLink(post);
+    }
+}

Added: feeds100P26/src/action/org/jboss/blog/tools/PostFilterTools.java
===================================================================
--- feeds100P26/src/action/org/jboss/blog/tools/PostFilterTools.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/blog/tools/PostFilterTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,33 @@
+package org.jboss.blog.tools;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.post.PostFilter;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PostFilterTools {
+    public static void filterFromPostList(List<? extends RestrictedPost> posts, PostFilter filter) {
+        for (Iterator<? extends RestrictedPost> iter = posts.iterator(); iter.hasNext();) {
+            if (!filter.filter(iter.next())) {
+                iter.remove();
+            }
+        }
+    }
+
+    public static <T extends RestrictedPost> List<T> filterPostList(List<T> posts, PostFilter filter) {
+        List<T> filtered = new ArrayList<T>();
+
+        for (T post : posts) {
+            if (filter.filter(post)) {
+                filtered.add(post);
+            }
+        }
+
+        return filtered;
+    }
+}

Added: feeds100P26/src/action/org/jboss/shotoku/web/FilesystemResourceResolver.java
===================================================================
--- feeds100P26/src/action/org/jboss/shotoku/web/FilesystemResourceResolver.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/shotoku/web/FilesystemResourceResolver.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,32 @@
+package org.jboss.shotoku.web;
+
+import com.sun.facelets.impl.ResourceResolver;
+
+import javax.faces.context.FacesContext;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FilesystemResourceResolver implements ResourceResolver {
+    private String sourceBasePath;
+
+    public String getSourceBasePath() {
+        if (sourceBasePath == null) {
+            sourceBasePath = FacesContext.getCurrentInstance().getExternalContext()
+                    .getInitParameter("sourceBasePath");
+        }
+
+        return sourceBasePath;
+    }
+
+    public URL resolveUrl(String s) {
+        try {
+            return new URL("file", "", getSourceBasePath() + s);
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}

Added: feeds100P26/src/action/org/jboss/shotoku/web/ResourcesFilter.java
===================================================================
--- feeds100P26/src/action/org/jboss/shotoku/web/ResourcesFilter.java	                        (rev 0)
+++ feeds100P26/src/action/org/jboss/shotoku/web/ResourcesFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,189 @@
+/******************************************************************************
+ * JBoss, a division of Red Hat                                               *
+ * Copyright 2006, Red Hat Middleware, LLC, and individual                    *
+ * contributors as indicated by the @authors tag. See the                     *
+ * copyright.txt in the distribution for a full listing of                    *
+ * individual contributors.                                                   *
+ *                                                                            *
+ * This is free software; you can redistribute it and/or modify it            *
+ * under the terms of the GNU Lesser General Public License as                *
+ * published by the Free Software Foundation; either version 2.1 of           *
+ * the License, or (at your option) any later version.                        *
+ *                                                                            *
+ * This software is distributed in the hope that it will be useful,           *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU           *
+ * Lesser General Public License for more details.                            *
+ *                                                                            *
+ * You should have received a copy of the GNU Lesser General Public           *
+ * License along with this software; if not, write to the Free                *
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA         *
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.                   *
+ ******************************************************************************/
+package org.jboss.shotoku.web;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+/**
+ * A filter, which reads resources from the filesystem and makes them visible to the
+ * application as deployed files --- useful for development. Specifically,
+ * the <code>sourceBasePath</code> init-parameter value
+ * is prepended to the path. The file referenced by the path is then included
+ * in the request. To specify for which file extensions the filter is enabled,
+ * set the <code>extensions</code> init parameter. If not set, it defaults to:
+ * <code>jsp,css,html,htm,gif,jpg,jpeg,png,txt,xhtml</code>.
+ *
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ResourcesFilter implements Filter {
+    private final static Logger log = Logger.getLogger(ResourcesFilter.class.getName());
+
+    /**
+	 * A list of extensions, which are filtered by default, if nothing is
+	 * specified in the filter configuration.
+	 */
+	private final static String DEFAULT_EXTENSIONS = "jsp,css,html,htm,gif,jpg,jpeg,png,txt,xhtml";
+
+	/**
+	 * Base path to a directory where files will
+	 * be copied; it's a subdirectory of a deployment directory created by the
+	 * app server.
+	 */
+	private String destBasePath;
+
+	/**
+	 * Directory in the filesystem from which to read the files.
+	 */
+	private String sourceBasePath;
+
+	/**
+	 * A set of <code>java.lang.String</code>s, which are extensions, that are filtered.
+	 */
+	private Set<String> extensions;
+
+    /**
+	 * Transfers all bytes from the given input stream to the given output
+	 * stream.
+	 *
+	 * @param is
+	 *            Input stream to read from.
+	 * @param os
+	 *            Output stream to write to.
+	 * @throws java.io.IOException In case of an IO exception.
+	 */
+	private void transfer(InputStream is, OutputStream os) throws IOException {
+		byte[] buffer = new byte[1024];
+		int read;
+		while ((read = is.read(buffer)) != -1) {
+			os.write(buffer, 0, read);
+		}
+	}
+
+	public void init(FilterConfig conf) {
+		sourceBasePath = conf.getInitParameter("sourceBasePath");
+        if (sourceBasePath == null || "".equals(sourceBasePath)) {
+            sourceBasePath = conf.getServletContext().getInitParameter("sourceBasePath");
+        }
+
+        destBasePath = conf.getServletContext().getRealPath("");
+
+		extensions = new HashSet<String>();
+		String filteredExtensionsString = conf.getInitParameter("extensions");
+
+		if (filteredExtensionsString == null) {
+			filteredExtensionsString = DEFAULT_EXTENSIONS;
+		}
+
+		String[] tokens = filteredExtensionsString.split(",");
+
+        extensions.addAll(Arrays.asList(tokens));
+	}
+
+	private String safeToString(Object o) {
+		if (o == null) {
+			return null;
+		}
+
+		return o.toString();
+	}
+
+	private boolean checkExtension(String path) {
+		int dotIndex = path.lastIndexOf('.');
+
+		if (dotIndex != -1) {
+			String extension = path.substring(dotIndex + 1);
+			return extensions.contains(extension);
+		} else {
+			return false;
+		}
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response,
+			FilterChain chain) throws IOException, ServletException {
+		if (request instanceof HttpServletRequest) {
+			HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+			/* Getting the name of the requested resource; first checking if
+			 * it is an included, then forwarded resource. Finally, checking
+			 * the request uri itself. */
+			String requestedResource;
+			requestedResource = safeToString(httpRequest.getAttribute("javax.servlet.include.servlet_path"));
+
+			if (requestedResource == null) {
+				requestedResource = httpRequest.getServletPath();
+			}
+
+            // JSF check - we have to replace .jsf with .jsp.
+			String realRequestedResource = requestedResource;
+			if (realRequestedResource.endsWith(".jsf")) {
+				realRequestedResource = realRequestedResource.replace(".jsf", ".jsp");
+			} else if (realRequestedResource.endsWith(".seam")) {
+				realRequestedResource = realRequestedResource.replace(".seam", ".xhtml");
+			}
+
+			// Filtering only some file extensions. Not filtering Seam's debug.xhtml.
+			if ((!checkExtension(realRequestedResource)) || (realRequestedResource.indexOf("debug.xhtml") != -1)) {
+				chain.doFilter(request, response);
+				return;
+			}
+
+            File sourceFile = new File(sourceBasePath + realRequestedResource);
+			File destFile = new File(destBasePath + realRequestedResource);
+
+			InputStream in = null;
+			OutputStream out = null;
+
+			try {
+				destFile.getParentFile().mkdirs();
+				destFile.setLastModified(System.currentTimeMillis());
+
+				in = new FileInputStream(sourceFile);
+				out = new FileOutputStream(destFile);
+
+				transfer(in, out);
+			} catch (Exception e) {
+                log.warning("Cannot copy resource: " + sourceFile);
+            } finally {
+				if (in != null) {
+					in.close();
+				}
+
+				if (out != null) {
+					out.close();
+				}
+			}
+		}
+
+        chain.doFilter(request, response);
+    }
+
+	public void destroy() {
+
+	}
+}
\ No newline at end of file

Added: feeds100P26/src/model/org/jboss/blog/model/Category.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/Category.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/Category.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,73 @@
+package org.jboss.blog.model;
+
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class Category implements RestrictedCategory {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @NotEmpty
+    @Column(unique = true)
+    private String name;
+
+    public Category(String name) {
+        this.name = name;
+    }
+
+    public Category() {
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Category)) return false;
+
+        Category category = (Category) o;
+
+        if (id != null ? !id.equals(category.id) : category.id != null) return false;
+        if (name != null ? !name.equals(category.name) : category.name != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/Enclosure.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/Enclosure.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/Enclosure.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,105 @@
+package org.jboss.blog.model;
+
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.validator.Length;
+import org.hibernate.validator.NotNull;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class Enclosure implements RestrictedEnclosure {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @ManyToOne(optional = false, fetch = FetchType.LAZY)
+    @NotNull
+    private Post post;
+
+    @NotEmpty
+    @Length(max = 512)
+    private String url;
+
+    @Column
+    @NotNull
+    private long length;
+
+    @Length(max = 128)
+    @NotNull
+    private String type;
+
+    public Enclosure() { }
+
+    public Enclosure(Post post, String url, long length, String type) {
+        this.post = post;
+        this.url = url;
+        this.length = length;
+        this.type = type;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Post getPost() {
+        return post;
+    }
+
+    public void setPost(Post post) {
+        this.post = post;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public long getLength() {
+        return length;
+    }
+
+    public void setLength(long length) {
+        this.length = length;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Enclosure)) return false;
+
+        Enclosure enclosure = (Enclosure) o;
+
+        if (id != null ? !id.equals(enclosure.id) : enclosure.id != null) return false;
+        if (url != null ? !url.equals(enclosure.url) : enclosure.url != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (url != null ? url.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/Group.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/Group.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/Group.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,99 @@
+package org.jboss.blog.model;
+
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.validator.Pattern;
+import org.hibernate.validator.Length;
+import org.hibernate.validator.Email;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Table(name = "FeedGroup")
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class Group implements Serializable {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @NotEmpty
+    @Column(unique = true)
+    @Length(max = 32)
+    @Pattern(regex = "^[a-z0-9_]*$", message = "#{messages['blog.group.invalidname']}")
+    private String name;
+
+    @NotEmpty
+    @Length(max = 64)
+    private String displayName;
+
+    @Lob
+    private String header;
+
+    @Email
+    private String adminEmail;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+
+    public void setHeader(String header) {
+        this.header = header;
+    }
+
+    public String getAdminEmail() {
+        return adminEmail;
+    }
+
+    public void setAdminEmail(String adminEmail) {
+        this.adminEmail = adminEmail;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Group)) return false;
+
+        Group group = (Group) o;
+
+        if (id != null ? !id.equals(group.id) : group.id != null) return false;
+        if (name != null ? !name.equals(group.name) : group.name != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/Image.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/Image.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/Image.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,101 @@
+package org.jboss.blog.model;
+
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.validator.Length;
+import org.hibernate.validator.NotNull;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class Image implements RestrictedImage {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @ManyToOne(optional = false, fetch = FetchType.LAZY)
+    @NotNull
+    private Post post;
+
+    @NotEmpty
+    @Length(max = 512)
+    private String url;
+
+    @Length(max = 512)
+    private String title;
+
+    @Length(max = 512)
+    private String link;
+
+    public Image() { }
+
+    public Image(Post post, String url) {
+        this.post = post;
+        this.url = url;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Post getPost() {
+        return post;
+    }
+
+    public void setPost(Post post) {
+        this.post = post;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Image)) return false;
+
+        Image image = (Image) o;
+
+        if (id != null ? !id.equals(image.id) : image.id != null) return false;
+        if (url != null ? !url.equals(image.url) : image.url != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (url != null ? url.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/Post.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/Post.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/Post.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,230 @@
+package org.jboss.blog.model;
+
+import org.hibernate.search.annotations.*;
+import org.hibernate.validator.Length;
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.validator.NotNull;
+import org.hibernate.validator.Pattern;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.jboss.blog.tools.search.bridge.StripHtmlBridge;
+import org.jboss.blog.tools.search.bridge.FeedBridge;
+import org.jboss.blog.tools.search.UnrestrictedFeedFilter;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.blog.tools.GeneralTools;
+import org.jboss.blog.model.feed.Feed;
+
+import javax.persistence.*;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Indexed
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+ at FullTextFilterDef(name = "unrestrictedFeedFilter", impl = UnrestrictedFeedFilter.class)
+public class Post implements RestrictedPost {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    @DocumentId
+    private Integer id;
+
+    @NotEmpty
+    @Length(max = 512)
+    @Field
+    @Boost(2)
+    private String title;
+
+    @NotEmpty
+    @Pattern(regex = "^[a-z0-9_]*$")
+    @Column(unique = true)
+    private String titleAsId;
+
+    @Lob
+    @Field(bridge = @FieldBridge(impl = StripHtmlBridge.class))
+    private String content;
+
+    @Length(max = 512)
+    @NotEmpty
+    private String link;
+
+    @Length(max = 256)
+    @Field
+    private String author;
+
+    @ManyToMany
+    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+    private List<Category> categories;
+
+    @Temporal(value = TemporalType.TIMESTAMP)
+    @NotNull
+    private Date published;
+
+    @Temporal(value = TemporalType.TIMESTAMP)
+    @NotNull
+    private Date modified;
+
+    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
+    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+    private List<Enclosure> enclosures;
+
+    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
+    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+    private List<Image> images;
+
+    @ManyToOne
+    @NotNull
+    @Field(name = "restricted", bridge = @FieldBridge(impl = FeedBridge.class))
+    private Feed feed;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        setTitleAsId(StringTools.convertTitleToLink(title));
+        this.title = title;
+    }
+
+    public String getTitleAsId() {
+        return titleAsId;
+    }
+
+    public void setTitleAsId(String titleAsId) {
+        this.titleAsId = titleAsId;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    public void setAuthor(String author) {
+        this.author = author;
+    }
+
+    public List<Category> getCategories() {
+        return categories;
+    }
+
+    public void setCategories(List<Category> categories) {
+        this.categories = categories;
+    }
+
+    public Date getPublished() {
+        return published;
+    }
+
+    public void setPublished(Date published) {
+        this.published = published;
+    }
+
+    public Date getModified() {
+        return modified;
+    }
+
+    public void setModified(Date modified) {
+        this.modified = modified;
+    }
+
+    public List<Enclosure> getEnclosures() {
+        return enclosures;
+    }
+
+    public void setEnclosures(List<Enclosure> enclosures) {
+        this.enclosures = enclosures;
+    }
+
+    public List<Image> getImages() {
+        return images;
+    }
+
+    public void setImages(List<Image> images) {
+        this.images = images;
+    }
+
+    public Feed getFeed() {
+        return feed;
+    }
+
+    public void setFeed(Feed feed) {
+        this.feed = feed;
+    }
+
+    @Transient
+    public String getEffectiveAuthor() {
+        String postAuthor = getAuthor();
+        switch (getFeed().getPostAuthorType()) {
+            case POST_AUTHOR:
+                return postAuthor == null ? "" : postAuthor;
+
+            case BLOG_AUTHOR:
+                return getFeed().getAuthor();
+
+            case BLOG_AUTHOR_IF_MISSING:
+                return StringTools.isEmpty(postAuthor) ? getFeed().getAuthor() : postAuthor;
+        }
+
+        return null;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Post)) return false;
+
+        Post post = (Post) o;
+
+        if (id != null ? !id.equals(post.id) : post.id != null) return false;
+        if (title != null ? !title.equals(post.title) : post.title != null) return false;
+        if (titleAsId != null ? !titleAsId.equals(post.titleAsId) : post.titleAsId != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (title != null ? title.hashCode() : 0);
+        result = 31 * result + (titleAsId != null ? titleAsId.hashCode() : 0);
+        return result;
+    }
+
+    public int compareTo(RestrictedPost post2) {
+        int result = - GeneralTools.compareDates(getPublished(), post2.getPublished());
+        if (result == 0) {
+            return GeneralTools.compareStrings(getTitle(), post2.getTitle());
+        } else {
+            return result;
+        }
+    }
+
+    public String toString() {
+        return "Post(title = " + title + ", published = " + published + ", titleAsId = " + titleAsId + ")";
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/RestrictedCategory.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/RestrictedCategory.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/RestrictedCategory.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,12 @@
+package org.jboss.blog.model;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RestrictedCategory extends Serializable {
+    Integer getId();
+
+    String getName();
+}

Added: feeds100P26/src/model/org/jboss/blog/model/RestrictedEnclosure.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/RestrictedEnclosure.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/RestrictedEnclosure.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,16 @@
+package org.jboss.blog.model;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RestrictedEnclosure extends Serializable {
+    Integer getId();
+
+    String getUrl();
+
+    long getLength();
+
+    String getType();
+}

Added: feeds100P26/src/model/org/jboss/blog/model/RestrictedImage.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/RestrictedImage.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/RestrictedImage.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,14 @@
+package org.jboss.blog.model;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RestrictedImage extends Serializable {
+    Integer getId();
+
+    String getUrl();
+
+    String getTitle();
+}

Added: feeds100P26/src/model/org/jboss/blog/model/RestrictedPost.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/RestrictedPost.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/RestrictedPost.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,38 @@
+package org.jboss.blog.model;
+
+import org.jboss.blog.model.feed.RestrictedFeed;
+
+import java.util.List;
+import java.util.Date;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RestrictedPost extends Comparable<RestrictedPost>, Serializable {
+    Integer getId();
+
+    String getTitle();
+
+    String getTitleAsId();
+
+    String getContent();
+
+    String getLink();
+
+    String getAuthor();
+
+    List<? extends RestrictedCategory> getCategories();
+
+    Date getPublished();
+
+    Date getModified();
+
+    List<? extends RestrictedEnclosure> getEnclosures();
+
+    List<? extends RestrictedImage> getImages();
+
+    RestrictedFeed getFeed();
+
+    String getEffectiveAuthor();
+}

Added: feeds100P26/src/model/org/jboss/blog/model/Template.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/Template.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/Template.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,104 @@
+package org.jboss.blog.model;
+
+import org.hibernate.validator.Length;
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
+import java.util.Date;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class Template implements Serializable {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @Length(max = 32)
+    @NotEmpty
+    @Column(unique = true)
+    private String name;
+
+    @Lob
+    @NotEmpty
+    private String text;
+
+    @Enumerated
+    private XmlType type;
+
+    @Temporal(value = TemporalType.TIMESTAMP)
+    private Date lastModified;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public XmlType getType() {
+        return type;
+    }
+
+    public void setType(XmlType type) {
+        this.type = type;
+    }
+
+    public Date getLastModified() {
+        return lastModified;
+    }
+
+    public void setLastModified(Date lastModified) {
+        this.lastModified = lastModified;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Template)) return false;
+
+        Template template = (Template) o;
+
+        if (type != template.type) return false;
+        if (id != null ? !id.equals(template.id) : template.id != null) return false;
+        if (name != null ? !name.equals(template.name) : template.name != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        return result;
+    }
+
+    @PrePersist
+    @PreUpdate
+    public void updateLastModified() {
+        setLastModified(new Date());
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/XmlType.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/XmlType.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/XmlType.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+package org.jboss.blog.model;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public enum XmlType {
+    ATOM("application/atom+xml"),
+    RSS2("application/xhtml+xml"),
+    RSS1("application/xhtml+xml");
+
+    private final String contentType;
+
+    public String contentType() {
+        return contentType;
+    }
+
+    XmlType(String contentType) {
+        this.contentType = contentType;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/configuration/Configuration.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/configuration/Configuration.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/configuration/Configuration.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,99 @@
+package org.jboss.blog.model.configuration;
+
+import org.hibernate.validator.NotNull;
+import org.hibernate.validator.Email;
+
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class Configuration implements Serializable {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @Column
+    @NotNull
+    private int readTimeout;
+
+    @Column
+    @NotNull
+    private int connectionTimeout;
+
+    @Column
+    @NotNull
+    private int updateInterval;
+
+    @Column
+    @Email
+    private String adminEmail;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public int getReadTimeout() {
+        return readTimeout;
+    }
+
+    public void setReadTimeout(int readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+
+    public int getConnectionTimeout() {
+        return connectionTimeout;
+    }
+
+    public void setConnectionTimeout(int connectionTimeout) {
+        this.connectionTimeout = connectionTimeout;
+    }
+
+    public int getUpdateInterval() {
+        return updateInterval;
+    }
+
+    public void setUpdateInterval(int updateInterval) {
+        this.updateInterval = updateInterval;
+    }
+
+    public String getAdminEmail() {
+        return adminEmail;
+    }
+
+    public void setAdminEmail(String adminEmail) {
+        this.adminEmail = adminEmail;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Configuration)) return false;
+
+        Configuration that = (Configuration) o;
+
+        if (connectionTimeout != that.connectionTimeout) return false;
+        if (readTimeout != that.readTimeout) return false;
+        if (updateInterval != that.updateInterval) return false;
+        if (adminEmail != null ? !adminEmail.equals(that.adminEmail) : that.adminEmail != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + readTimeout;
+        result = 31 * result + connectionTimeout;
+        result = 31 * result + updateInterval;
+        result = 31 * result + (adminEmail != null ? adminEmail.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/feed/AggregatedFeed.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/AggregatedFeed.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/AggregatedFeed.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,62 @@
+package org.jboss.blog.model.feed;
+
+import org.jboss.blog.model.post.PostFilter;
+import org.jboss.blog.model.Group;
+import org.hibernate.validator.NotNull;
+import org.hibernate.annotations.CollectionOfElements;
+import org.hibernate.annotations.MapKeyManyToMany;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.Entity;
+import javax.persistence.Basic;
+import javax.persistence.Lob;
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class AggregatedFeed extends Feed {
+    @CollectionOfElements
+    @MapKeyManyToMany
+    @Lob
+    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+    private Map<Feed, PostFilter> feeds;
+
+    @CollectionOfElements
+    @MapKeyManyToMany
+    @Lob
+    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+    private Map<Group, PostFilter> groups;
+
+    @NotNull
+    @Basic
+    @Lob
+    private PostFilter globalFilter;
+
+    public Map<Feed, PostFilter> getFeeds() {
+        return feeds;
+    }
+
+    public void setFeeds(Map<Feed, PostFilter> feeds) {
+        this.feeds = feeds;
+    }
+
+    public Map<Group, PostFilter> getGroups() {
+        return groups;
+    }
+
+    public void setGroups(Map<Group, PostFilter> groups) {
+        this.groups = groups;
+    }
+
+    public PostFilter getGlobalFilter() {
+        return globalFilter;
+    }
+
+    public void setGlobalFilter(PostFilter globalFilter) {
+        this.globalFilter = globalFilter;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/feed/Feed.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/Feed.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/Feed.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,261 @@
+package org.jboss.blog.model.feed;
+
+import org.hibernate.validator.Length;
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.validator.Pattern;
+import org.hibernate.validator.NotNull;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.model.Template;
+import org.jboss.blog.model.Group;
+
+import javax.persistence.*;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class Feed implements RestrictedFeed {
+    @Id
+    @GeneratedValue(strategy = GenerationType.TABLE)
+    @Column(updatable = false)
+    private Integer id;
+
+    @NotNull
+    @ManyToOne
+    private Group group;
+
+    @NotEmpty
+    @Length(max = 32)
+    @Column(unique = true)
+    @Pattern(regex = "^[a-z0-9_]*$", message = "#{messages['blog.feed.new.invalidname']}")
+    private String name;
+
+    @NotEmpty
+    @Length(max = 512)
+    private String title;
+
+    @NotEmpty
+    @Length(max = 256)
+    private String author;
+
+    @Length(max = 512)
+    private String link;
+
+    @OneToMany(cascade = {CascadeType.REMOVE}, mappedBy = "feed")
+    private List<Post> posts;
+
+    @Lob
+    private String description;
+
+    @ManyToMany
+    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+    private Map<XmlType, Template> templates;
+
+    @Column
+    private int maxPostsInFeed;
+
+    @Column
+    private int maxPostsOnPage;
+  
+    @Column
+    @NotNull
+    private PostAuthorType postAuthorType;
+
+    @Column
+    @NotNull
+    private boolean accepted;
+
+    @Column
+    private Boolean showDigg;
+
+    @Column
+    private Boolean showDzone;
+
+    @Column
+    private Boolean showDelicious;
+
+    @Column
+    private Boolean showStumble;
+
+    @Column
+    private Boolean restricted;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Group getGroup() {
+        return group;
+    }
+
+    public void setGroup(Group group) {
+        this.group = group;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    public void setAuthor(String author) {
+        this.author = author;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    public void setLink(String link) {
+        this.link = link;
+    }
+
+    public List<Post> getPosts() {
+        return posts;
+    }
+
+    public void setPosts(List<Post> posts) {
+        this.posts = posts;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Map<XmlType, Template> getTemplates() {
+        return templates;
+    }
+
+    public void setTemplates(Map<XmlType, Template> templates) {
+        this.templates = templates;
+    }
+
+    public int getMaxPostsInFeed() {
+        return maxPostsInFeed;
+    }
+
+    public void setMaxPostsInFeed(int maxPostsInFeed) {
+        this.maxPostsInFeed = maxPostsInFeed;
+    }
+
+    public int getMaxPostsOnPage() {
+        return maxPostsOnPage;
+    }
+
+    public void setMaxPostsOnPage(int maxPostsOnPage) {
+        this.maxPostsOnPage = maxPostsOnPage;
+    }
+
+    public PostAuthorType getPostAuthorType() {
+        return postAuthorType;
+    }
+
+    public void setPostAuthorType(PostAuthorType postAuthorType) {
+        this.postAuthorType = postAuthorType;
+    }
+
+    public boolean isAccepted() {
+        return accepted;
+    }
+
+    public void setAccepted(boolean accepted) {
+        this.accepted = accepted;
+    }
+
+    public Boolean getShowDigg() {
+        return showDigg;
+    }
+
+    public void setShowDigg(Boolean showDigg) {
+        this.showDigg = showDigg;
+    }
+
+    public Boolean getShowDzone() {
+        return showDzone;
+    }
+
+    public void setShowDzone(Boolean showDzone) {
+        this.showDzone = showDzone;
+    }
+
+    public Boolean getShowDelicious() {
+        return showDelicious;
+    }
+
+    public void setShowDelicious(Boolean showDelicious) {
+        this.showDelicious = showDelicious;
+    }
+
+    public Boolean getShowStumble() {
+        return showStumble;
+    }
+
+    public void setShowStumble(Boolean showStumble) {
+        this.showStumble = showStumble;
+    }
+
+    public Boolean getRestricted() {
+        return restricted;
+    }
+
+    public void setRestricted(Boolean restricted) {
+        this.restricted = restricted;
+    }
+
+    @Transient
+    public boolean isFeedRestricted() {
+        if (restricted == null || !restricted) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Feed)) return false;
+
+        Feed feed = (Feed) o;
+
+        if (id != null ? !id.equals(feed.id) : feed.id != null) return false;
+        if (name != null ? !name.equals(feed.name) : feed.name != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/feed/HighlightsFeed.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/HighlightsFeed.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/HighlightsFeed.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,27 @@
+package org.jboss.blog.model.feed;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.jboss.blog.model.Post;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToMany;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class HighlightsFeed extends Feed {
+    @ManyToMany
+    private List<Post> selectedPosts;
+
+    public List<Post> getSelectedPosts() {
+        return selectedPosts;
+    }
+
+    public void setSelectedPosts(List<Post> selectedPosts) {
+        this.selectedPosts = selectedPosts;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostInfo.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostInfo.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostInfo.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,73 @@
+package org.jboss.blog.model.feed;
+
+import org.jboss.blog.model.Post;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.ManyToOne;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class IndividualPostInfo {
+    @Id
+    @GeneratedValue
+    private int id;
+
+    @ManyToOne
+    private IndividualPostsFeed feed;
+
+    @ManyToOne
+    private Post post;
+
+    private String remoteFeedAddress;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public IndividualPostsFeed getFeed() {
+        return feed;
+    }
+
+    public void setFeed(IndividualPostsFeed feed) {
+        this.feed = feed;
+    }
+
+    public Post getPost() {
+        return post;
+    }
+
+    public void setPost(Post post) {
+        this.post = post;
+    }
+
+    public String getRemoteFeedAddress() {
+        return remoteFeedAddress;
+    }
+
+    public void setRemoteFeedAddress(String remoteFeedAddress) {
+        this.remoteFeedAddress = remoteFeedAddress;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof IndividualPostInfo)) return false;
+
+        IndividualPostInfo that = (IndividualPostInfo) o;
+
+        if (id != that.id) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return id;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostsFeed.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostsFeed.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/IndividualPostsFeed.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,27 @@
+package org.jboss.blog.model.feed;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.CascadeType;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class IndividualPostsFeed extends Feed {
+    @OneToMany(mappedBy = "feed", cascade = CascadeType.ALL)
+    private List<IndividualPostInfo> postInfos;
+
+    public List<IndividualPostInfo> getPostInfos() {
+        return postInfos;
+    }
+
+    public void setPostInfos(List<IndividualPostInfo> postInfos) {
+        this.postInfos = postInfos;
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/model/org/jboss/blog/model/feed/PostAuthorType.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/PostAuthorType.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/PostAuthorType.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,10 @@
+package org.jboss.blog.model.feed;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public enum PostAuthorType {
+    BLOG_AUTHOR_IF_MISSING,
+    POST_AUTHOR,
+    BLOG_AUTHOR
+}

Added: feeds100P26/src/model/org/jboss/blog/model/feed/RemoteFeed.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/RemoteFeed.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/RemoteFeed.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,65 @@
+package org.jboss.blog.model.feed;
+
+import org.hibernate.validator.Length;
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.jboss.blog.tools.validator.Regexp;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.blog.model.post.PostFilter;
+import org.jboss.blog.model.post.filter.CategoryRegexpFilter;
+import org.jboss.blog.model.post.filter.TotalFilter;
+
+import javax.persistence.Entity;
+import javax.persistence.Column;
+import javax.persistence.Transient;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class RemoteFeed extends Feed {
+    @NotEmpty
+    @Length(max = 512)
+    private String remoteLink;
+
+    @Regexp
+    @Column
+    private String includeCategoryRegexp;
+
+    @Transient
+    private PostFilter includeCategoryFilter;
+
+    public String getRemoteLink() {
+        return remoteLink;
+    }
+
+    public void setRemoteLink(String remoteLink) {
+        this.remoteLink = remoteLink;
+    }
+
+    public String getIncludeCategoryRegexp() {
+        return includeCategoryRegexp;
+    }
+
+    public void setIncludeCategoryRegexp(String includeCategoryRegexp) {
+        this.includeCategoryRegexp = includeCategoryRegexp;
+
+        synchronized(this) {
+            includeCategoryFilter = null;
+        }
+    }
+
+    public synchronized PostFilter getIncludeCategoryFilter() {
+        if (includeCategoryFilter == null) {
+            if (StringTools.isEmpty(includeCategoryRegexp)) {
+                includeCategoryFilter = new TotalFilter();
+            } else {
+                includeCategoryFilter = new CategoryRegexpFilter(includeCategoryRegexp);
+            }
+        }
+        
+        return includeCategoryFilter;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/feed/RestrictedFeed.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/feed/RestrictedFeed.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/feed/RestrictedFeed.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,32 @@
+package org.jboss.blog.model.feed;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RestrictedFeed extends Serializable {
+    Integer getId();
+
+    String getName();
+
+    String getTitle();
+
+    String getAuthor();
+
+    String getLink();
+
+    String getDescription();
+
+    int getMaxPostsInFeed();
+
+    int getMaxPostsOnPage();
+
+    Boolean getShowDigg();
+
+    Boolean getShowDzone();
+
+    Boolean getShowDelicious();
+
+    boolean isFeedRestricted();
+}

Added: feeds100P26/src/model/org/jboss/blog/model/log/PropositionsLog.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/log/PropositionsLog.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/log/PropositionsLog.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,95 @@
+package org.jboss.blog.model.log;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.security.SecurityUser;
+import org.hibernate.validator.NotNull;
+
+import javax.persistence.*;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Table(uniqueConstraints = @UniqueConstraint(columnNames = "feed_id"))
+public class PropositionsLog {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @NotNull
+    @ManyToOne
+    private Feed feed;
+
+    @NotNull
+    @ManyToOne
+    private SecurityUser securityUser;
+
+    @NotNull
+    @Temporal(TemporalType.TIMESTAMP)
+    private Date date;
+
+    public PropositionsLog() { }
+
+    public PropositionsLog(Feed feed, SecurityUser securityUser, Date date) {
+        this.feed = feed;
+        this.securityUser = securityUser;
+        this.date = date;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Feed getFeed() {
+        return feed;
+    }
+
+    public void setFeed(Feed feed) {
+        this.feed = feed;
+    }
+
+    public SecurityUser getSecurityUser() {
+        return securityUser;
+    }
+
+    public void setSecurityUser(SecurityUser securityUser) {
+        this.securityUser = securityUser;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PropositionsLog)) return false;
+
+        PropositionsLog that = (PropositionsLog) o;
+
+        if (date != null ? !date.equals(that.date) : that.date != null) return false;
+        if (feed != null ? !feed.equals(that.feed) : that.feed != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (securityUser != null ? !securityUser.equals(that.securityUser) : that.securityUser != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (feed != null ? feed.hashCode() : 0);
+        result = 31 * result + (securityUser != null ? securityUser.hashCode() : 0);
+        result = 31 * result + (date != null ? date.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/PostFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/PostFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/PostFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,12 @@
+package org.jboss.blog.model.post;
+
+import org.jboss.blog.model.RestrictedPost;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface PostFilter extends Serializable {
+    public boolean filter(RestrictedPost post);
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/filter/AbstractRegexpFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/filter/AbstractRegexpFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/filter/AbstractRegexpFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,48 @@
+package org.jboss.blog.model.post.filter;
+
+import org.jboss.blog.model.post.PostFilter;
+import org.jboss.blog.tools.validator.Regexp;
+
+import java.util.regex.Pattern;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public abstract class AbstractRegexpFilter implements PostFilter  {
+    @Regexp
+    protected String regexp;
+
+    protected transient Pattern pattern;
+
+    protected AbstractRegexpFilter() {
+    }
+
+    protected AbstractRegexpFilter(String regexp) {
+        setRegexp(regexp);
+    }
+
+    public String getRegexp() {
+        return regexp;
+    }
+
+    public void setRegexp(String regexp) {
+        this.regexp = regexp;
+
+        pattern = Pattern.compile(this.regexp);
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CategoryRegexpFilter)) return false;
+
+        AbstractRegexpFilter that = (AbstractRegexpFilter) o;
+
+        if (regexp != null ? !regexp.equals(that.regexp) : that.regexp != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (regexp != null ? regexp.hashCode() : 0);
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/filter/AndFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/filter/AndFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/filter/AndFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,71 @@
+package org.jboss.blog.model.post.filter;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.post.PostFilter;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AndFilter implements PostFilter {
+    private static final long serialVersionUID = 6716831026867502343L;
+
+    private List<PostFilter> filters;
+
+    public AndFilter() {
+        filters = new ArrayList<PostFilter>();
+    }
+
+    public AndFilter(List<PostFilter> filters) {
+        this.filters = filters;
+    }
+
+    public List<PostFilter> getFilters() {
+        return filters;
+    }
+
+    public void setFilters(List<PostFilter> filters) {
+        this.filters = filters;
+    }
+
+    public boolean filter(RestrictedPost post) {
+        for (PostFilter filter : filters) {
+            if (!filter.filter(post)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append("And(");
+        for (Iterator<PostFilter> iter = filters.iterator(); iter.hasNext();) {
+            result.append(iter.next().toString());
+            if (iter.hasNext()) {
+                result.append(", ");
+            }
+        }
+
+        return result.append(")").toString();
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AndFilter)) return false;
+
+        AndFilter andFilter = (AndFilter) o;
+
+        if (filters != null ? !filters.equals(andFilter.filters) : andFilter.filters != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return (filters != null ? filters.hashCode() : 0);
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/filter/AuthorRegexpFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/filter/AuthorRegexpFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/filter/AuthorRegexpFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,32 @@
+package org.jboss.blog.model.post.filter;
+
+import org.jboss.blog.model.RestrictedPost;
+
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AuthorRegexpFilter extends AbstractRegexpFilter {
+    private static final long serialVersionUID = 2015864901762113142L;
+
+    public boolean filter(RestrictedPost post) {
+        return pattern.matcher(post.getEffectiveAuthor()).matches();
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out)
+            throws IOException {
+        out.defaultWriteObject();
+    }
+
+    private void readObject(java.io.ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+
+        setRegexp(getRegexp());
+    }
+
+    public String toString() {
+        return "Author matching regexp: " + regexp;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/filter/CategoryRegexpFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/filter/CategoryRegexpFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/filter/CategoryRegexpFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,46 @@
+package org.jboss.blog.model.post.filter;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.RestrictedCategory;
+
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class CategoryRegexpFilter extends AbstractRegexpFilter {
+    private static final long serialVersionUID = 1209854919382113132L;
+
+    public CategoryRegexpFilter() {
+    }
+
+    public CategoryRegexpFilter(String regexp) {
+        super(regexp);
+    }
+
+    public boolean filter(RestrictedPost post) {
+        for (RestrictedCategory cat : post.getCategories()) {
+            if (pattern.matcher(cat.getName()).matches()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void writeObject(java.io.ObjectOutputStream out)
+            throws IOException {
+        out.defaultWriteObject();
+    }
+
+    private void readObject(java.io.ObjectInputStream in)
+            throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+
+        setRegexp(getRegexp());
+    }
+
+    public String toString() {
+        return "Category matching regexp: " + regexp;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/filter/NotPodcastFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/filter/NotPodcastFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/filter/NotPodcastFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,30 @@
+package org.jboss.blog.model.post.filter;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.post.PostFilter;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class NotPodcastFilter implements PostFilter {
+    private static final long serialVersionUID = 3962316121420952911L;
+
+    public boolean filter(RestrictedPost post) {
+        return post.getEnclosures() == null || post.getEnclosures().size() == 0;
+    }
+
+    public String toString() {
+        return "No enclosure (not a podcast entry)";
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof NotPodcastFilter)) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return NotPodcastFilter.class.hashCode();
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/filter/PodcastFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/filter/PodcastFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/filter/PodcastFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,30 @@
+package org.jboss.blog.model.post.filter;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.post.PostFilter;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PodcastFilter implements PostFilter {
+    private static final long serialVersionUID = -1115632975507749056L;
+
+    public boolean filter(RestrictedPost post) {
+        return post.getEnclosures() != null && post.getEnclosures().size() != 0;
+    }
+
+    public String toString() {
+        return "With enclosure (a podcast entry)";
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof PodcastFilter)) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return PodcastFilter.class.hashCode();
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/post/filter/TotalFilter.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/post/filter/TotalFilter.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/post/filter/TotalFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,30 @@
+package org.jboss.blog.model.post.filter;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.post.PostFilter;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TotalFilter implements PostFilter {
+    private static final long serialVersionUID = -8839681871861904116L;
+
+    public boolean filter(RestrictedPost post) {
+        return true;
+    }
+
+    public String toString() {
+        return "Everything";
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TotalFilter)) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        return TotalFilter.class.hashCode();
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/security/FeedsSecurityRole.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/security/FeedsSecurityRole.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/security/FeedsSecurityRole.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,11 @@
+package org.jboss.blog.model.security;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public enum FeedsSecurityRole {
+    ADMIN,
+    GROUP_ADMIN,
+    FEED_ADMIN,
+    VIEW
+}

Added: feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityGroup.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityGroup.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityGroup.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,14 @@
+package org.jboss.blog.model.security;
+
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RestrictedSecurityGroup extends Serializable {
+    String getExternalId();
+
+    Object getRealGroup();
+
+    void setRealGroup(Object realGroup);
+}

Added: feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityUser.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityUser.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/security/RestrictedSecurityUser.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,12 @@
+package org.jboss.blog.model.security;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface RestrictedSecurityUser {
+    String getExternalId();
+
+    Object getRealUser();
+
+    void setRealUser(Object realUser);
+}

Added: feeds100P26/src/model/org/jboss/blog/model/security/SecurityGroup.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/security/SecurityGroup.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/security/SecurityGroup.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,82 @@
+package org.jboss.blog.model.security;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SecurityGroup implements RestrictedSecurityGroup {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @Column(unique = true)
+    private String externalId;
+
+    @ManyToMany(mappedBy = "groups")
+    private List<SecurityMapping> mappings;
+
+    @Transient
+    private Object realGroup;
+
+    public SecurityGroup() { }
+
+    public SecurityGroup(Object realGroup, String externalId) {
+        this.realGroup = realGroup;
+        this.externalId = externalId;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getExternalId() {
+        return externalId;
+    }
+
+    public void setExternalId(String externalId) {
+        this.externalId = externalId;
+    }
+
+    public List<SecurityMapping> getMappings() {
+        return mappings;
+    }
+
+    public void setMappings(List<SecurityMapping> mappings) {
+        this.mappings = mappings;
+    }
+
+    public Object getRealGroup() {
+        return realGroup;
+    }
+
+    public void setRealGroup(Object realGroup) {
+        this.realGroup = realGroup;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecurityGroup)) return false;
+
+        SecurityGroup that = (SecurityGroup) o;
+
+        if (externalId != null ? !externalId.equals(that.externalId) : that.externalId != null) return false;
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (externalId != null ? externalId.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/security/SecurityMapping.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/security/SecurityMapping.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/security/SecurityMapping.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,92 @@
+package org.jboss.blog.model.security;
+
+import org.hibernate.validator.NotNull;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Table(uniqueConstraints = @UniqueConstraint(columnNames = {"role", "idForRole"}))
+public class SecurityMapping {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @Column
+    @NotNull
+    private FeedsSecurityRole role;
+
+    @Column
+    private Integer idForRole;
+
+    @ManyToMany
+    private List<SecurityGroup> groups;
+
+    @ManyToMany
+    private List<SecurityUser> users;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public FeedsSecurityRole getRole() {
+        return role;
+    }
+
+    public void setRole(FeedsSecurityRole role) {
+        this.role = role;
+    }
+
+    public Integer getIdForRole() {
+        return idForRole;
+    }
+
+    public void setIdForRole(Integer idForRole) {
+        this.idForRole = idForRole;
+    }
+
+    public List<SecurityGroup> getGroups() {
+        return groups;
+    }
+
+    public void setGroups(List<SecurityGroup> groups) {
+        this.groups = groups;
+    }
+
+    public List<SecurityUser> getUsers() {
+        return users;
+    }
+
+    public void setUsers(List<SecurityUser> users) {
+        this.users = users;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecurityMapping)) return false;
+
+        SecurityMapping that = (SecurityMapping) o;
+
+        if (id != null ? !id.equals(that.id) : that.id != null) return false;
+        if (idForRole != null ? !idForRole.equals(that.idForRole) : that.idForRole != null) return false;
+        if (role != that.role) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (role != null ? role.hashCode() : 0);
+        result = 31 * result + (idForRole != null ? idForRole.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/model/org/jboss/blog/model/security/SecurityUser.java
===================================================================
--- feeds100P26/src/model/org/jboss/blog/model/security/SecurityUser.java	                        (rev 0)
+++ feeds100P26/src/model/org/jboss/blog/model/security/SecurityUser.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,94 @@
+package org.jboss.blog.model.security;
+
+import org.hibernate.validator.NotNull;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+public class SecurityUser implements RestrictedSecurityUser {
+    @Id
+    @GeneratedValue
+    @Column(updatable = false)
+    private Integer id;
+
+    @NotNull
+    private String externalId;
+
+    private String restrictedKey;
+
+    @ManyToMany(mappedBy = "users")
+    private List<SecurityMapping> mappings;
+
+    @Transient
+    private Object realUser;
+
+    public SecurityUser() { }
+
+    public SecurityUser(Object realUser, String externalId) {
+        this.externalId = externalId;
+        this.realUser = realUser;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getExternalId() {
+        return externalId;
+    }
+
+    public void setExternalId(String externalId) {
+        this.externalId = externalId;
+    }
+
+    public Object getRealUser() {
+        return realUser;
+    }
+
+    public void setRealUser(Object realUser) {
+        this.realUser = realUser;
+    }
+
+    public List<SecurityMapping> getMappings() {
+        return mappings;
+    }
+
+    public void setMappings(List<SecurityMapping> mappings) {
+        this.mappings = mappings;
+    }
+
+    public String getRestrictedKey() {
+        return restrictedKey;
+    }
+
+    public void setRestrictedKey(String restrictedKey) {
+        this.restrictedKey = restrictedKey;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SecurityUser)) return false;
+
+        SecurityUser user = (SecurityUser) o;
+
+        if (externalId != null ? !externalId.equals(user.externalId) : user.externalId != null) return false;
+        if (id != null ? !id.equals(user.id) : user.id != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = (id != null ? id.hashCode() : 0);
+        result = 31 * result + (externalId != null ? externalId.hashCode() : 0);
+        return result;
+    }
+}

Added: feeds100P26/src/portal/org/jboss/blog/portlet/BlogPortlet.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/portlet/BlogPortlet.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/portlet/BlogPortlet.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,78 @@
+package org.jboss.blog.portlet;
+
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.LinkService;
+import org.jboss.blog.service.FeedNotFoundException;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.seam.Component;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.contexts.Lifecycle;
+
+import javax.portlet.*;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class BlogPortlet extends GenericPortlet {
+    protected void doView(RenderRequest request, RenderResponse response)
+            throws PortletException, IOException {
+        boolean createContexts = !Contexts.isEventContextActive() && !Contexts.isApplicationContextActive();
+        if (createContexts) {
+            Lifecycle.beginCall();
+        }
+
+        try {
+            // Getting the services
+            FeedsService feedsService = (FeedsService) Component.getInstance("feedsService");
+            LinkService linkService = (LinkService) Component.getInstance("linkService");
+
+            // Reading the preferences
+            PortletPreferences preferences = request.getPreferences();
+            String feedName = preferences.getValue("feedName", null);
+
+            String numberOfPostsPerPageString = preferences.getValue("numberOfPosts", "5");
+            int numberOfPostsPerPage = Integer.parseInt(numberOfPostsPerPageString);
+
+            String summaryLengthString = preferences.getValue("summaryLength", "200");
+            Integer summaryLength = Integer.valueOf(summaryLengthString);
+
+            String showDateString = preferences.getValue("showDate", "true");
+            Boolean showDate = Boolean.valueOf(showDateString);
+
+            String jsp = preferences.getValue("jsp", "/view.jsp");
+
+            response.setContentType("text/html");
+
+            try {
+                Feed feed = feedsService.getFeed(feedName);
+
+                // Reading posts
+                List<? extends RestrictedPost> posts = feedsService.getPosts(feed, 0, numberOfPostsPerPage);
+
+                // Setting request attributes
+                request.setAttribute("feed", feed);
+                request.setAttribute("posts", posts);
+                request.setAttribute("summaryLength", summaryLength);
+                request.setAttribute("showDate", showDate);
+                request.setAttribute("linkService", linkService);
+
+                response.setTitle(feed.getTitle());
+                // Including the jsp
+                PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(jsp);
+                rd.include(request, response);
+            } catch (FeedNotFoundException e) {
+                request.setAttribute("feedName", feedName);
+
+                PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher("/feed_not_found.jsp");
+                rd.include(request, response);
+            }
+        } finally {
+            if (createContexts) {
+                Lifecycle.endCall();
+            }
+        }
+    }
+}

Added: feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalExternalSecurityService.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalExternalSecurityService.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalExternalSecurityService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,186 @@
+package org.jboss.blog.session.security.external;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.security.SecurityUser;
+import org.jboss.blog.model.security.SecurityGroup;
+import org.jboss.blog.model.security.RestrictedSecurityGroup;
+import org.jboss.blog.model.security.RestrictedSecurityUser;
+import org.jboss.blog.session.security.InvalidLoginException;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("externalSecurityService")
+ at AutoCreate
+ at Scope(ScopeType.STATELESS)
+public class PortalExternalSecurityService extends AbstractExternalSecurityService {
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private PortalSecurityService portalSecurityService;
+
+    protected EntityManager getEntityManager() {
+        return entityManager;
+    }
+
+    @Logger
+    private Log log;
+
+    private List<SecurityGroup> convertRoles(List<PortalRole> roles, boolean getUnrestricted) {
+        List<SecurityGroup> securityGroups = new ArrayList<SecurityGroup>();
+
+        for (PortalRole role : roles) {
+            SecurityGroup securityGroup = new SecurityGroup(role, role.getId());
+            if (getUnrestricted) {
+                securityGroups.add(getUnrestrictedSecurityGroup(securityGroup));
+            } else {
+                securityGroups.add(securityGroup);
+            }
+        }
+
+        return securityGroups;
+    }
+
+    private List<SecurityUser> convertUsers(List<PortalUser> users, boolean getUnrestricted) {
+        List<SecurityUser> securityUsers = new ArrayList<SecurityUser>();
+
+        for (PortalUser user : users) {
+            SecurityUser securityUser = new SecurityUser(user, user.getId());
+            if (getUnrestricted) {
+                securityUsers.add(getUnrestrictedSecurityUser(securityUser));
+            } else {
+                securityUsers.add(securityUser);
+            }
+        }
+
+        return securityUsers;
+    }
+
+    private PortalRole getRealRole(RestrictedSecurityGroup securityGroup) throws PortalSecurityException {
+        if (securityGroup.getRealGroup() == null) {
+            PortalRole role = portalSecurityService.getRoleById(securityGroup.getExternalId());
+            securityGroup.setRealGroup(role);
+
+            return role;
+        } else {
+            return (PortalRole) securityGroup.getRealGroup();
+        }
+    }
+
+    private PortalUser getRealUser(RestrictedSecurityUser securityUser) throws PortalSecurityException {
+        if (securityUser.getRealUser() == null) {
+            PortalUser user = portalSecurityService.getUserById(securityUser.getExternalId());
+            securityUser.setRealUser(user);
+
+            return user;
+        } else {
+            return (PortalUser) securityUser.getRealUser();
+        }
+    }
+
+    public SecurityUser authenticate(String username, String password) throws InvalidLoginException {
+        try {
+            PortalUser portalUser = portalSecurityService.authenticate(username, password);
+
+            SecurityUser securityUser = new SecurityUser();
+            securityUser.setExternalId(portalUser.getId());
+            securityUser.setRealUser(portalUser);
+
+            return getUnrestrictedSecurityUser(securityUser);
+        } catch (PortalSecurityException e) {
+            log.error(e);
+            throw new InvalidLoginException();
+        }
+    }
+
+    public List<? extends RestrictedSecurityGroup> getAllGroups() {
+        try {
+            //noinspection unchecked
+            return convertRoles(portalSecurityService.getAllRoles(), false);
+        } catch (PortalSecurityException e) {
+            log.error(e);
+            return new ArrayList<RestrictedSecurityGroup>();
+        }
+    }
+
+    public List<? extends RestrictedSecurityUser> getUsers(int start, int count) {
+        try {
+            //noinspection unchecked
+            return convertUsers(portalSecurityService.getUsers(start, count), false);
+        } catch (PortalSecurityException e) {
+            log.error(e);
+            return new ArrayList<RestrictedSecurityUser>();
+        }
+    }
+
+    public List<? extends RestrictedSecurityUser> getUsers(String filter, int start, int count) {
+        try {
+            //noinspection unchecked
+            return convertUsers(portalSecurityService.getUsers(filter, start, count), false);
+        } catch (PortalSecurityException e) {
+            log.error(e);
+            return new ArrayList<RestrictedSecurityUser>();
+        }
+    }
+
+    public List<SecurityGroup> getGroupsOfUser(SecurityUser securityUser) {
+        try {
+            //noinspection unchecked
+            return convertRoles(portalSecurityService.getRolesOfUser(securityUser.getExternalId()), true);
+        } catch (PortalSecurityException e) {
+            log.error(e);
+            return new ArrayList<SecurityGroup>();
+        }
+    }
+
+    public List<? extends RestrictedSecurityUser> getUsersInGroup(RestrictedSecurityGroup securityGroup) {
+        try {
+            return convertUsers(portalSecurityService.getUsersInRole(securityGroup.getExternalId()), false);
+        } catch (PortalSecurityException e) {
+            log.error(e);
+            return new ArrayList<RestrictedSecurityUser>();
+        }
+    }
+
+    public String getEmail(RestrictedSecurityUser securityUser) {
+        try {
+            return getRealUser(securityUser).getEmail();
+        } catch (PortalSecurityException e) {
+            log.error(e);
+            return null;
+        }
+    }
+
+    public String getDisplayName(RestrictedSecurityUser securityUser) {
+        try {
+            return getRealUser(securityUser).getDisplayName();
+        } catch (PortalSecurityException e) {
+            return "<? " + e.getMessage() + ">";
+        }
+    }
+
+    public String getDisplayName(RestrictedSecurityGroup securityGroup) {
+        try {
+            return getRealRole(securityGroup).getDisplayName();
+        } catch (PortalSecurityException e) {
+            return "<? " + e.getMessage() + ">";
+        }
+    }
+
+    public RestrictedSecurityGroup getAdminGroup() {
+        try {
+            PortalRole portalRole = portalSecurityService.getAdminRole();
+
+            return new SecurityGroup(portalRole, portalRole.getId());
+        } catch (PortalSecurityException e) {
+            return null;
+        }
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalRole.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalRole.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalRole.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,22 @@
+package org.jboss.blog.session.security.external;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PortalRole {
+    private String id;
+    private String displayName;
+
+    public PortalRole(String id, String displayName) {
+        this.id = id;
+        this.displayName = displayName;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+}

Added: feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityException.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityException.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,23 @@
+package org.jboss.blog.session.security.external;
+
+import javax.naming.NamingException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PortalSecurityException extends Exception {
+    public PortalSecurityException() {
+    }
+
+    public PortalSecurityException(String message) {
+        super(message);
+    }
+
+    public PortalSecurityException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public PortalSecurityException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityService.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityService.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,32 @@
+package org.jboss.blog.session.security.external;
+
+import org.jboss.blog.session.security.InvalidLoginException;
+
+import javax.ejb.Local;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Local
+public interface PortalSecurityService {
+    PortalUser authenticate(String username, String password) throws PortalSecurityException, InvalidLoginException;
+
+    List<PortalRole> getAllRoles() throws PortalSecurityException;
+
+    public List<PortalUser> getUsers(int start, int count) throws PortalSecurityException;
+
+    public List<PortalUser> getUsers(String filter, int start, int count) throws PortalSecurityException;
+
+    List<PortalRole> getRolesOfUser(String id) throws PortalSecurityException;
+
+    List<PortalUser> getUsersInRole(String id) throws PortalSecurityException;
+
+    PortalRole getAdminRole() throws PortalSecurityException;
+
+    PortalRole getRoleById(String id) throws PortalSecurityException;
+
+    PortalUser getUserById(String id) throws PortalSecurityException;
+
+    void remove();
+}

Added: feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityServiceImpl.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityServiceImpl.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalSecurityServiceImpl.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,218 @@
+package org.jboss.blog.session.security.external;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ejb.Remove;
+import javax.ejb.Stateless;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.jboss.blog.session.security.InvalidLoginException;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.portal.identity.IdentityException;
+import org.jboss.portal.identity.MembershipModule;
+import org.jboss.portal.identity.NoSuchUserException;
+import org.jboss.portal.identity.Role;
+import org.jboss.portal.identity.RoleModule;
+import org.jboss.portal.identity.User;
+import org.jboss.portal.identity.UserModule;
+import org.jboss.portal.identity.UserProfileModule;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Logger;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.log.Log;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Stateless
+ at Name("portalSecurityService")
+ at AutoCreate
+public class PortalSecurityServiceImpl implements PortalSecurityService {
+    @Logger
+    private Log log;
+
+    private UserModule getUserModule() throws PortalSecurityException {
+        try {
+            return (UserModule) new InitialContext().lookup("java:portal/UserModule");
+        } catch (NamingException e) {
+            log.error(e);
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    private RoleModule getRoleModule() throws PortalSecurityException {
+        try {
+            return (RoleModule) new InitialContext().lookup("java:portal/RoleModule");
+        } catch (NamingException e) {
+            log.error(e);
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    private MembershipModule getMembershipModule() throws PortalSecurityException {
+        try {
+            return (MembershipModule) new InitialContext().lookup("java:portal/MembershipModule");
+        } catch (NamingException e) {
+            log.error(e);
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    private UserProfileModule getProfileModule() throws PortalSecurityException {
+        try {
+            return (UserProfileModule) new InitialContext().lookup("java:portal/UserProfileModule");
+        } catch (NamingException e) {
+            log.error(e);
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    private String getUserEmail(User user) throws PortalSecurityException {
+        try {
+            return StringTools.safeToString(getProfileModule().getProperty(user, User.INFO_USER_EMAIL_REAL));
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public PortalUser authenticate(String username, String password)
+            throws InvalidLoginException, PortalSecurityException {
+        User user;
+        try {
+            user = getUserModule().findUserByUserName(username);
+        } catch (NoSuchUserException e) {
+            throw new InvalidLoginException();
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+
+        if (!user.validatePassword(password)) {
+           throw new InvalidLoginException();
+        }
+
+        return new PortalUser(user.getId().toString(), user.getUserName(), getUserEmail(user));
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public List<PortalRole> getAllRoles() throws PortalSecurityException {
+        try {
+            List<PortalRole> roles = new ArrayList<PortalRole>();
+
+            for (Object roleObj : getRoleModule().findRoles()) {
+                Role role = (Role) roleObj;
+                roles.add(new PortalRole(role.getId().toString(), role.getDisplayName()));
+            }
+
+            return roles;
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public List<PortalUser> getUsers(int start, int count) throws PortalSecurityException {
+        try {
+            List<PortalUser> users = new ArrayList<PortalUser>();
+
+            for (Object userObj : getUserModule().findUsers(start, count)) {
+                User user = (User) userObj;
+                users.add(new PortalUser(user.getId().toString(), user.getUserName(), getUserEmail(user)));
+            }
+
+            return users;
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public List<PortalUser> getUsers(String filter, int start, int count) throws PortalSecurityException {
+        try {
+            List<PortalUser> users = new ArrayList<PortalUser>();
+
+            for (Object userObj : getUserModule().findUsersFilteredByUserName(filter, start, count)) {
+                User user = (User) userObj;
+                users.add(new PortalUser(user.getId().toString(), user.getUserName(), getUserEmail(user)));
+            }
+
+            return users;
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public List<PortalRole> getRolesOfUser(String id) throws PortalSecurityException {
+        try {
+            List<PortalRole> roles = new ArrayList<PortalRole>();
+
+            for (Object roleObj : getMembershipModule().getRoles(getUserModule().findUserById(id)))  {
+                Role role = (Role) roleObj;
+                roles.add(new PortalRole(role.getId().toString(), role.getDisplayName()));
+            }
+
+            return roles;
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public List<PortalUser> getUsersInRole(String id) throws PortalSecurityException {
+        try {
+            List<PortalUser> users = new ArrayList<PortalUser>();
+
+            for (Object roleObj : getMembershipModule().getUsers(getRoleModule().findRoleById(id)))  {
+                User user = (User) roleObj;
+                users.add(new PortalUser(user.getId().toString(), user.getUserName(), getUserEmail(user)));
+            }
+
+            return users;
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public PortalRole getAdminRole() throws PortalSecurityException {
+        try {
+            Role adminRole = getRoleModule().findRoleByName("Admin");
+
+            if (adminRole == null) {
+                return null;
+            }
+
+            return new PortalRole(adminRole.getId().toString(), adminRole.getDisplayName());
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public PortalRole getRoleById(String id) throws PortalSecurityException {
+        try {
+            Role role = getRoleModule().findRoleById(id);
+            return new PortalRole(role.getId().toString(), role.getDisplayName());
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+    public PortalUser getUserById(String id) throws PortalSecurityException {
+        try {
+            User user = getUserModule().findUserById(id);
+            return new PortalUser(user.getId().toString(), user.getUserName(), getUserEmail(user));
+        } catch (IdentityException e) {
+            throw new PortalSecurityException(e);
+        }
+    }
+
+    @Remove
+    public void remove() {  }
+}

Added: feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalUser.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalUser.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/session/security/external/PortalUser.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,28 @@
+package org.jboss.blog.session.security.external;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PortalUser {
+    private String id;
+    private String displayName;
+    private String email;
+
+    public PortalUser(String id, String displayName, String email) {
+        this.id = id;
+        this.displayName = displayName;
+        this.email = email;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+}

Added: feeds100P26/src/portal/org/jboss/blog/session/security/external/SecurityBootstrap.java
===================================================================
--- feeds100P26/src/portal/org/jboss/blog/session/security/external/SecurityBootstrap.java	                        (rev 0)
+++ feeds100P26/src/portal/org/jboss/blog/session/security/external/SecurityBootstrap.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,45 @@
+package org.jboss.blog.session.security.external;
+
+import org.jboss.seam.annotations.*;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.session.security.SecurityModBean;
+import org.jboss.blog.model.security.SecurityGroup;
+import org.jboss.blog.model.security.FeedsSecurityRole;
+import org.jboss.blog.model.security.RestrictedSecurityGroup;
+
+import javax.persistence.EntityManager;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("securityBootstrap")
+ at Scope(ScopeType.STATELESS)
+public class SecurityBootstrap {
+    @In
+    private SecurityModBean securityMod;
+
+    @In
+    private ExternalSecurityService externalSecurityService;
+
+    @In
+    private EntityManager entityManager;
+    
+    @Observer("org.jboss.blog.postBlogInit")
+    @Transactional    
+    public void initAdministrator() {
+        List<SecurityGroup> administratorGroups = securityMod.getAdministratorGroups();
+        if (administratorGroups == null || administratorGroups.size() == 0) {
+            RestrictedSecurityGroup group = externalSecurityService.getAdminGroup();
+            
+            if (group != null) {
+                securityMod.setRole(FeedsSecurityRole.ADMIN);
+                securityMod.setRestrictedSecurityGroup(group);
+
+                securityMod.addSecurityGroupAsSuperUser();
+
+                entityManager.flush();
+            }
+        }
+    }
+}

Added: feeds100P26/src/services/org/jboss/blog/service/FeedNotFoundException.java
===================================================================
--- feeds100P26/src/services/org/jboss/blog/service/FeedNotFoundException.java	                        (rev 0)
+++ feeds100P26/src/services/org/jboss/blog/service/FeedNotFoundException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.service;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FeedNotFoundException extends Exception {
+    public FeedNotFoundException() {
+    }
+
+    public FeedNotFoundException(String message) {
+        super(message);
+    }
+
+    public FeedNotFoundException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public FeedNotFoundException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/services/org/jboss/blog/service/FeedsService.java
===================================================================
--- feeds100P26/src/services/org/jboss/blog/service/FeedsService.java	                        (rev 0)
+++ feeds100P26/src/services/org/jboss/blog/service/FeedsService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,31 @@
+package org.jboss.blog.service;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.feed.RestrictedFeed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.RestrictedPost;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface FeedsService {
+    Post getPost(String titleAsId) throws PostNotFoundException;
+
+    Feed getFeed(String feedName) throws FeedNotFoundException;
+
+    List<? extends RestrictedPost> getPosts(RestrictedFeed feed, int from, int to);
+
+    /**
+     *
+     * @param feed Feed of which posts to get.
+     * @param from Starting post.
+     * @param to Ending post.
+     * @param restricted Should restricted posts be included.
+     * @return A list of posts of the given feed, of length max. to-from.
+     */
+    List<? extends RestrictedPost> getPosts(RestrictedFeed feed, int from, int to, boolean restricted);
+
+    List<? extends RestrictedPost> getPosts(int from, int to);
+}

Added: feeds100P26/src/services/org/jboss/blog/service/GroupsService.java
===================================================================
--- feeds100P26/src/services/org/jboss/blog/service/GroupsService.java	                        (rev 0)
+++ feeds100P26/src/services/org/jboss/blog/service/GroupsService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.service;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Group;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface GroupsService {
+    List<Group> getAllGroups();
+
+    List<Feed> acceptedFeeds(Group group);
+
+    List<Feed> unacceptedFeeds(Group group);
+
+    List<Feed> restrictedFeeds(Group group);
+
+    List<Feed> allAcceptedFeeds(Group group);
+}

Added: feeds100P26/src/services/org/jboss/blog/service/LinkService.java
===================================================================
--- feeds100P26/src/services/org/jboss/blog/service/LinkService.java	                        (rev 0)
+++ feeds100P26/src/services/org/jboss/blog/service/LinkService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,24 @@
+package org.jboss.blog.service;
+
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.XmlType;
+import org.jboss.blog.model.Post;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface LinkService {
+    String getServerAddress();
+
+    void setServerAddress(String serverAddress);
+
+    String getContextName();
+
+    void setContextName(String contextName);
+
+    String generateFeedLink(Feed feed, XmlType type);
+
+    String generateFeedPageLink(Feed feed);
+
+    String generatePostLink(Post post);
+}

Added: feeds100P26/src/services/org/jboss/blog/service/PostNotFoundException.java
===================================================================
--- feeds100P26/src/services/org/jboss/blog/service/PostNotFoundException.java	                        (rev 0)
+++ feeds100P26/src/services/org/jboss/blog/service/PostNotFoundException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.service;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PostNotFoundException extends Exception {
+    public PostNotFoundException() {
+    }
+
+    public PostNotFoundException(String message) {
+        super(message);
+    }
+
+    public PostNotFoundException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public PostNotFoundException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/shotoku/org/jboss/blog/model/shotoku/ShotokuFeed.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/blog/model/shotoku/ShotokuFeed.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/blog/model/shotoku/ShotokuFeed.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,51 @@
+package org.jboss.blog.model.shotoku;
+
+import org.jboss.blog.model.feed.Feed;
+import org.hibernate.validator.NotNull;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.Entity;
+import javax.persistence.Column;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Entity
+ at Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+public class ShotokuFeed extends Feed {
+    @NotNull
+    @Column
+    private String cmId;
+
+    @NotNull
+    @Column
+    private String cmPath;
+
+    @Column
+    private String podcastPrefix;
+
+    public String getCmId() {
+        return cmId;
+    }
+
+    public void setCmId(String cmId) {
+        this.cmId = cmId;
+    }
+
+    public String getCmPath() {
+        return cmPath;
+    }
+
+    public void setCmPath(String cmPath) {
+        this.cmPath = cmPath;
+    }
+
+    public String getPodcastPrefix() {
+        return podcastPrefix;
+    }
+
+    public void setPodcastPrefix(String podcastPrefix) {
+        this.podcastPrefix = podcastPrefix;
+    }
+}

Added: feeds100P26/src/shotoku/org/jboss/blog/session/feed/dao/ShotokuFeedDao.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/blog/session/feed/dao/ShotokuFeedDao.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/blog/session/feed/dao/ShotokuFeedDao.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,36 @@
+package org.jboss.blog.session.feed.dao;
+
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.shotoku.ShotokuFeed;
+import org.jboss.blog.session.feed.posts.DatabaseFeedPosts;
+import org.jboss.blog.session.feed.type.FeedType;
+import org.jboss.blog.session.feed.update.ShotokuFeedUpdate;
+import org.jboss.blog.session.update.UpdateException;
+import org.jboss.seam.Component;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at FeedType(
+        name = "shotoku",
+        addPage = "/manage/shotoku/shotoku_add.xhtml",
+        editPage = "/manage/shotoku/shotoku_edit.xhtml",
+        model = ShotokuFeed.class)
+public class ShotokuFeedDao implements FeedDao {
+    private ShotokuFeed shotokuFeed;
+
+    public ShotokuFeedDao(ShotokuFeed shotokuFeed) {
+        this.shotokuFeed = shotokuFeed;
+    }
+
+    public List<? extends RestrictedPost> getPosts(int from, int to, boolean restricted) {
+        return ((DatabaseFeedPosts) Component.getInstance("databaseFeedPosts")).getPosts(
+                shotokuFeed, from, to);
+    }
+
+    public void update() throws UpdateException {
+        ((ShotokuFeedUpdate) Component.getInstance("shotokuFeedUpdate")).update(shotokuFeed);
+    }
+}

Added: feeds100P26/src/shotoku/org/jboss/blog/session/feed/mod/ShotokuModBean.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/blog/session/feed/mod/ShotokuModBean.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/blog/session/feed/mod/ShotokuModBean.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,133 @@
+package org.jboss.blog.session.feed.mod;
+
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.security.Restrict;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.core.Events;
+import org.jboss.blog.model.shotoku.ShotokuFeed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.session.feed.InvalidFeedTypeException;
+import org.jboss.blog.session.shotoku.ShotokuFeedService;
+import org.jboss.blog.session.shotoku.PostContentTooLargeException;
+import org.jboss.blog.session.shotoku.ShotokuException;
+import org.jboss.shotoku.exceptions.RepositoryException;
+
+import javax.persistence.EntityManager;
+import javax.faces.application.FacesMessage;
+import java.util.List;
+import java.io.Serializable;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.CONVERSATION)
+ at Name("shotokuFeedMod")
+public class ShotokuModBean implements Serializable {
+    @In
+    private FeedModBean feedMod;
+
+    @In
+    private EntityManager entityManager;
+
+    @In
+    private FacesMessages facesMessages;
+
+    @In
+    private PostsValidator postsValidator;
+
+    @In
+    private ShotokuFeedService shotokuFeedService;
+
+    private ShotokuFeed shotokuFeed;
+
+    private Boolean podcast;
+
+    private boolean pathOk;
+    private Exception pathException;
+
+    private List<Post> posts;
+
+    public ShotokuFeed getShotokuFeed() throws InvalidFeedTypeException {
+        if (shotokuFeed == null) {
+            if (feedMod.getFeed() == null) {
+                shotokuFeed = new ShotokuFeed();
+                feedMod.initNewFeed(shotokuFeed);
+
+                shotokuFeed.setCmId("default");
+            } else {
+                if (feedMod.getFeed() instanceof ShotokuFeed) {
+                    shotokuFeed = (ShotokuFeed) feedMod.getFeed();
+                } else {
+                    throw new InvalidFeedTypeException();
+                }
+            }
+        }
+
+        return shotokuFeed;
+    }
+
+    public boolean isPathOk() {
+        return pathOk;
+    }
+
+    public void setPathOk(boolean pathOk) {
+        this.pathOk = pathOk;
+    }
+
+    public Exception getPathException() {
+        return pathException;
+    }
+
+    public void setPathException(Exception pathException) {
+        this.pathException = pathException;
+    }
+
+    public boolean isPodcast() throws InvalidFeedTypeException {
+        if (podcast == null) {
+            podcast = getShotokuFeed().getPodcastPrefix() != null;
+        }
+        
+        return podcast;
+    }
+
+    public void setPodcast(boolean podcast) {
+        this.podcast = podcast;
+    }
+
+    public void checkPath() throws InvalidFeedTypeException {
+        try {
+            posts = shotokuFeedService.getPosts(getShotokuFeed());
+
+            if (!postsValidator.validatePosts(posts, false, "cmPath")) {
+                throw new RepositoryException("Some files are missing required properties.");
+            }
+
+            setPathOk(true);
+        } catch (ShotokuException e) {
+            setPathOk(false);
+            setPathException(e);
+        } catch (PostContentTooLargeException e) {
+            setPathOk(false);
+            setPathException(e);
+        }
+    }
+
+    public void saveNew() throws InvalidFeedTypeException {
+        getShotokuFeed().setPosts(posts);
+    }
+
+    @Restrict("#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}")
+    public void saveExisting() throws InvalidFeedTypeException {
+        shotokuFeedService.refreshEnclosureLinks(getShotokuFeed());
+
+        entityManager.flush();
+
+        facesMessages.addFromResourceBundle(FacesMessage.SEVERITY_INFO, "blog.feed.updated",
+                getShotokuFeed().getName());
+
+        Events.instance().raiseEvent("org.jboss.blog.feed.updated", getShotokuFeed());
+    }
+}

Added: feeds100P26/src/shotoku/org/jboss/blog/session/feed/update/ShotokuFeedUpdate.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/blog/session/feed/update/ShotokuFeedUpdate.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/blog/session/feed/update/ShotokuFeedUpdate.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,53 @@
+package org.jboss.blog.session.feed.update;
+
+import org.jboss.blog.model.shotoku.ShotokuFeed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.session.merge.MergeServiceBean;
+import org.jboss.blog.session.update.UpdateException;
+import org.jboss.blog.session.feed.lock.FeedsLocksBean;
+import org.jboss.blog.session.shotoku.ShotokuFeedService;
+import org.jboss.blog.session.shotoku.PostContentTooLargeException;
+import org.jboss.blog.session.shotoku.ShotokuException;
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+
+import java.util.concurrent.locks.Lock;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Name("shotokuFeedUpdate")
+ at Scope(ScopeType.STATELESS)
+public class ShotokuFeedUpdate {
+    @In
+    private ShotokuFeedService shotokuFeedService;
+
+    @In
+    private MergeServiceBean mergeService;
+
+    @In
+    private FeedsLocksBean feedsLocks;
+
+    public void update(ShotokuFeed feed) throws UpdateException {
+        Lock feedLock = feedsLocks.getLockForFeed(feed.getName());
+        feedLock.lock();
+        try {
+            List<Post> posts;
+
+            try {
+                posts = shotokuFeedService.getPosts(feed);
+            } catch (ShotokuException e) {
+                throw new UpdateException(e);
+            } catch (PostContentTooLargeException e) {
+                throw new UpdateException(e);
+            }
+
+            mergeService.merge(feed, posts);
+        } finally {
+            feedLock.unlock();
+        }
+    }
+}

Added: feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/PostContentTooLargeException.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/PostContentTooLargeException.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/PostContentTooLargeException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.session.shotoku;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class PostContentTooLargeException extends Exception {
+    public PostContentTooLargeException() {
+    }
+
+    public PostContentTooLargeException(String message) {
+        super(message);
+    }
+
+    public PostContentTooLargeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public PostContentTooLargeException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuException.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuException.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,23 @@
+package org.jboss.blog.session.shotoku;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ShotokuException extends Exception {
+    public ShotokuException() {
+    }
+
+    public ShotokuException(String message) {
+        super(message);
+    }
+
+    public ShotokuException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ShotokuException(Throwable cause) {
+        super(cause);
+    }
+}
+
+

Added: feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuFeedService.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuFeedService.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/blog/session/shotoku/ShotokuFeedService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,108 @@
+package org.jboss.blog.session.shotoku;
+
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.ScopeType;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Enclosure;
+import org.jboss.blog.model.Image;
+import org.jboss.blog.model.Category;
+import org.jboss.blog.model.shotoku.ShotokuFeed;
+import org.jboss.blog.service.LinkService;
+import org.jboss.blog.tools.StringTools;
+import org.jboss.shotoku.ContentManager;
+import org.jboss.shotoku.Node;
+import org.jboss.shotoku.Directory;
+import org.jboss.shotoku.exceptions.RepositoryException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at Scope(ScopeType.STATELESS)
+ at Name("shotokuFeedService")
+ at AutoCreate
+public class ShotokuFeedService {
+    @In
+    private LinkService linkService;
+
+    private void setEnclosureUrl(ShotokuFeed feed, Enclosure enclosure, String nodeName) {
+        enclosure.setUrl(linkService.getServerAddress() + '/' + feed.getPodcastPrefix() + '/' + nodeName);
+    }
+
+    public void refreshEnclosureLinks(ShotokuFeed feed) {
+        for (Post post : feed.getPosts()) {
+            for (Enclosure enclosure : post.getEnclosures()) {
+                String currentUrl = enclosure.getUrl();
+                if (currentUrl != null) {
+                    int lastSlash = currentUrl.lastIndexOf('/');
+                    if ((lastSlash != -1) && (lastSlash != currentUrl.length()-1)) {
+                        setEnclosureUrl(feed, enclosure, currentUrl.substring(lastSlash+1));
+                    }
+                }
+            }
+        }
+    }
+
+    public List<Post> getPosts(ShotokuFeed feed) throws ShotokuException, PostContentTooLargeException {
+        try {
+            ContentManager cm = ContentManager.getContentManager(feed.getCmId(), feed.getCmPath());
+
+            List<Post> posts = new ArrayList<Post>();
+
+            if (cm == null) {
+                throw new ShotokuException("Couldn't initialize the specified content manager.");
+            }
+
+            Directory rootDir = cm.getRootDirectory();
+            if (rootDir == null) {
+                throw new ShotokuException("Couldn't get the root directory in the specified content manager.");
+            }
+
+            for (Node node : cm.getRootDirectory().getNodes()) {
+                Post post = new Post();
+
+                post.setAuthor(node.getProperty("author"));
+                post.setCategories(new ArrayList<Category>());
+                post.setEnclosures(new ArrayList<Enclosure>());
+                post.setImages(new ArrayList<Image>());
+                post.setModified(node.getLastModificationDate());
+                post.setPublished(node.getCreatedDate());
+                post.setTitle(node.getProperty("title"));
+
+                if (!StringTools.isEmpty(feed.getPodcastPrefix())) {
+                    post.setContent(node.getProperty("description"));
+
+                    Enclosure enclosure = new Enclosure(post, null, node.getLength(), node.getMimeType());
+                    setEnclosureUrl(feed, enclosure, node.getName());
+                    post.getEnclosures().add(enclosure);
+
+                    String thumbnail = node.getProperty("thumbnail");
+                    if (!StringTools.isEmpty(thumbnail)) {
+                        post.getImages().add(new Image(post, linkService.getServerAddress() + '/' + thumbnail));
+                    }
+                } else {
+                    if (node.getLength() > 1000000) {
+                        throw new PostContentTooLargeException("The content for node: '" + node.getFullName() +
+                                "' is too large for a post.");
+                    }
+
+                    post.setContent(node.getContent());
+                }
+
+                posts.add(post);
+            }
+
+            Collections.sort(posts);
+
+            return posts;
+        } catch (RepositoryException e) {
+            throw new ShotokuException(e);
+        }
+    }
+}

Added: feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuFilesystemResourceResolver.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuFilesystemResourceResolver.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuFilesystemResourceResolver.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,32 @@
+package org.jboss.shotoku.web;
+
+import com.sun.facelets.impl.ResourceResolver;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.jboss.shotoku.ContentManager;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ShotokuFilesystemResourceResolver implements ResourceResolver {
+    private String sourceBasePath;
+
+    public String getSourceBasePath() {
+        if (sourceBasePath == null) {
+            sourceBasePath = ContentManager.getProperty("shotoku.default.localpath") + "/feeds/view";
+        }
+
+        return sourceBasePath;
+    }
+
+    public URL resolveUrl(String s) {
+        try {
+            return new URL("file", "", getSourceBasePath() + s);
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuResourcesFilter.java
===================================================================
--- feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuResourcesFilter.java	                        (rev 0)
+++ feeds100P26/src/shotoku/org/jboss/shotoku/web/ShotokuResourcesFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,186 @@
+/******************************************************************************
+ * JBoss, a division of Red Hat                                               *
+ * Copyright 2006, Red Hat Middleware, LLC, and individual                    *
+ * contributors as indicated by the @authors tag. See the                     *
+ * copyright.txt in the distribution for a full listing of                    *
+ * individual contributors.                                                   *
+ *                                                                            *
+ * This is free software; you can redistribute it and/or modify it            *
+ * under the terms of the GNU Lesser General Public License as                *
+ * published by the Free Software Foundation; either version 2.1 of           *
+ * the License, or (at your option) any later version.                        *
+ *                                                                            *
+ * This software is distributed in the hope that it will be useful,           *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU           *
+ * Lesser General Public License for more details.                            *
+ *                                                                            *
+ * You should have received a copy of the GNU Lesser General Public           *
+ * License along with this software; if not, write to the Free                *
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA         *
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.                   *
+ ******************************************************************************/
+package org.jboss.shotoku.web;
+
+import org.jboss.shotoku.ContentManager;
+import org.jboss.shotoku.Node;
+import org.jboss.shotoku.exceptions.ResourceDoesNotExist;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+/**
+ * A filter, which reads resources from the filesystem and makes them visible to the
+ * application as deployed files --- useful for development. Specifically,
+ * the <code>sourceBasePath</code> init-parameter value
+ * is prepended to the path. The file referenced by the path is then included
+ * in the request. To specify for which file extensions the filter is enabled,
+ * set the <code>extensions</code> init parameter. If not set, it defaults to:
+ * <code>jsp,css,html,htm,gif,jpg,jpeg,png,txt,xhtml</code>.
+ *
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ShotokuResourcesFilter implements Filter {
+    private final static Logger log = Logger.getLogger(ResourcesFilter.class.getName());
+
+    /**
+	 * A list of extensions, which are filtered by default, if nothing is
+	 * specified in the filter configuration.
+	 */
+	private final static String DEFAULT_EXTENSIONS = "jsp,css,html,htm,gif,jpg,jpeg,png,txt";
+
+	/**
+	 * Base path to a directory where files will
+	 * be copied; it's a subdirectory of a deployment directory created by the
+	 * app server.
+	 */
+	private String destBasePath;
+
+	/**
+	 * A set of <code>java.lang.String</code>s, which are extensions, that are filtered.
+	 */
+	private Set<String> extensions;
+
+    private ContentManager cm;
+
+    /**
+	 * Transfers all bytes from the given input stream to the given output
+	 * stream.
+	 *
+	 * @param is
+	 *            Input stream to read from.
+	 * @param os
+	 *            Output stream to write to.
+	 * @throws java.io.IOException In case of an IO exception.
+	 */
+	private void transfer(InputStream is, OutputStream os) throws IOException {
+		byte[] buffer = new byte[1024];
+		int read;
+		while ((read = is.read(buffer)) != -1) {
+			os.write(buffer, 0, read);
+		}
+	}
+
+	public void init(FilterConfig conf) {
+        cm = ContentManager.getContentManager("default", "/feeds/view");
+
+        destBasePath = conf.getServletContext().getRealPath("");
+
+		extensions = new HashSet<String>();
+		String filteredExtensionsString = DEFAULT_EXTENSIONS;
+		String[] tokens = filteredExtensionsString.split(",");
+        extensions.addAll(Arrays.asList(tokens));
+	}
+
+	private String safeToString(Object o) {
+		if (o == null) {
+			return null;
+		}
+
+		return o.toString();
+	}
+
+	private boolean checkExtension(String path) {
+		int dotIndex = path.lastIndexOf('.');
+
+		if (dotIndex != -1) {
+			String extension = path.substring(dotIndex + 1);
+			return extensions.contains(extension);
+		} else {
+			return false;
+		}
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response,
+			FilterChain chain) throws IOException, ServletException {
+		if (request instanceof HttpServletRequest) {
+			HttpServletRequest httpRequest = (HttpServletRequest) request;
+
+			/* Getting the name of the requested resource; first checking if
+			 * it is an included, then forwarded resource. Finally, checking
+			 * the request uri itself. */
+			String requestedResource;
+			requestedResource = safeToString(httpRequest.getAttribute("javax.servlet.include.servlet_path"));
+
+			if (requestedResource == null) {
+				requestedResource = httpRequest.getServletPath();
+			}
+
+            // JSF check - we have to replace .jsf with .jsp.
+			String realRequestedResource = requestedResource;
+			if (realRequestedResource.endsWith(".jsf")) {
+				realRequestedResource = realRequestedResource.replace(".jsf", ".jsp");
+			}
+
+            // Filtering only some file extensions. Not filtering Seam's debug.xhtml.
+			if (!checkExtension(realRequestedResource)) {
+				chain.doFilter(request, response);
+				return;
+			}
+
+            Node sourceNode;
+            try {
+                sourceNode = cm.getNode(realRequestedResource);
+            } catch (ResourceDoesNotExist resourceDoesNotExist) {
+                chain.doFilter(request, response);
+                return;
+            }
+
+			File destFile = new File(destBasePath + realRequestedResource);
+
+			InputStream in = null;
+			OutputStream out = null;
+
+			try {
+				destFile.getParentFile().mkdirs();
+				destFile.setLastModified(System.currentTimeMillis());
+
+				in = sourceNode.getContentInputStream();
+				out = new FileOutputStream(destFile);
+
+				transfer(in, out);
+			} catch (Exception e) {
+                log.warning("Cannot copy resource: " + sourceNode);
+            } finally {
+				if (in != null) {
+					in.close();
+				}
+
+				if (out != null) {
+					out.close();
+				}
+			}
+		}
+
+        chain.doFilter(request, response);
+    }
+
+	public void destroy() {
+
+	}
+}
\ No newline at end of file

Added: feeds100P26/src/test/org/jboss/blog/session/feed/posts/AggregatedFeedPostsTest.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/feed/posts/AggregatedFeedPostsTest.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/feed/posts/AggregatedFeedPostsTest.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,156 @@
+package org.jboss.blog.session.feed.posts;
+
+import static org.easymock.EasyMock.*;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.jboss.blog.model.feed.AggregatedFeed;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.model.post.PostFilter;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.tools.TestTools;
+import org.jboss.blog.model.post.filter.TotalFilter;
+
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class AggregatedFeedPostsTest {
+    private Feed feed_0_posts;
+    private Feed feed_1_posts;
+    private Feed feed_2_posts;
+    private Feed feed_3_posts;
+    private Feed feed_4_posts;
+
+    private List<Post> list_0_posts;
+    private List<Post> list_1_posts;
+    private List<Post> list_2_posts;
+    private List<Post> list_3_posts;
+    private List<Post> list_4_posts;
+
+    private AggregatedFeed aggFeed1;
+    private AggregatedFeed aggFeed2;
+
+    private FeedsService mockFeedsService;
+    private AggregatedFeedPosts aggFeedPosts;
+
+    private void addPosts(List<Post> posts, int count) {
+        for (int i=0; i<count; i++) {
+            posts.add(new Post());
+        }
+    }
+
+    @BeforeMethod
+    public void setupFeeds() {
+        feed_0_posts = new Feed(); feed_0_posts.setId(0);
+        feed_1_posts = new Feed(); feed_1_posts.setId(1);
+        feed_2_posts = new Feed(); feed_2_posts.setId(2);
+        feed_3_posts = new Feed(); feed_3_posts.setId(3);
+        feed_4_posts = new Feed(); feed_4_posts.setId(4);
+
+        list_0_posts = new ArrayList<Post>();
+        list_1_posts = new ArrayList<Post>();
+        list_2_posts = new ArrayList<Post>();
+        list_3_posts = new ArrayList<Post>();
+        list_4_posts = new ArrayList<Post>();
+
+        addPosts(list_0_posts, 0);
+        addPosts(list_1_posts, 1);
+        addPosts(list_2_posts, 2);
+        addPosts(list_3_posts, 3);
+        addPosts(list_4_posts, 4);
+
+        aggFeed1 = new AggregatedFeed();
+        Map<Feed, PostFilter> feedsFilters1 = new HashMap<Feed, PostFilter>();
+        feedsFilters1.put(feed_0_posts, new TotalFilter());
+        feedsFilters1.put(feed_1_posts, new TotalFilter());
+        feedsFilters1.put(feed_2_posts, new TotalFilter());
+        feedsFilters1.put(feed_3_posts, new TotalFilter());
+        feedsFilters1.put(feed_4_posts, new TotalFilter());
+        aggFeed1.setFeeds(feedsFilters1);
+        aggFeed1.setGlobalFilter(new TotalFilter());
+
+        aggFeed2 = new AggregatedFeed();
+        aggFeed2.setFeeds(feedsFilters1);
+        aggFeed2.setGlobalFilter(new PostFilter() {
+            private static final long serialVersionUID = 8320081945645277412L;
+            public boolean filter(RestrictedPost post) {
+                return false;
+            }
+        });
+    }
+
+    @BeforeMethod
+    public void setupMocks() throws IllegalAccessException, NoSuchFieldException {
+        aggFeedPosts = new AggregatedFeedPosts();
+        mockFeedsService = createMock(FeedsService.class);
+
+        TestTools.setField(aggFeedPosts, "feedsService", mockFeedsService);
+    }
+
+    @Test
+    public void testGetLessThanAvailable() {
+        // Setup
+        expect(mockFeedsService.getPosts(feed_0_posts, 0, 2)).andReturn(new ArrayList(list_0_posts));
+        expect(mockFeedsService.getPosts(feed_1_posts, 0, 2)).andReturn(new ArrayList(list_1_posts));
+        expect(mockFeedsService.getPosts(feed_2_posts, 0, 2)).andReturn(new ArrayList(list_2_posts));
+        expect(mockFeedsService.getPosts(feed_3_posts, 0, 2)).andReturn(new ArrayList(list_2_posts));
+        expect(mockFeedsService.getPosts(feed_4_posts, 0, 2)).andReturn(new ArrayList(list_2_posts));
+
+        // Test
+        replay(mockFeedsService);
+        List<? extends RestrictedPost> posts = aggFeedPosts.getPosts(aggFeed1, 0, 2, false);
+
+        // Check
+        verify(mockFeedsService);
+        assert posts.size() == 2;
+    }
+
+    @Test
+    public void testGetExactlyAvailable() {
+        // Setup
+        expect(mockFeedsService.getPosts(feed_0_posts, 0, 10)).andReturn(new ArrayList(list_0_posts));
+        expect(mockFeedsService.getPosts(feed_1_posts, 0, 10)).andReturn(new ArrayList(list_1_posts));
+        expect(mockFeedsService.getPosts(feed_2_posts, 0, 10)).andReturn(new ArrayList(list_2_posts));
+        expect(mockFeedsService.getPosts(feed_3_posts, 0, 10)).andReturn(new ArrayList(list_3_posts));
+
+        // TODO: why not ArrayList<Post>?
+
+        expect(mockFeedsService.getPosts(feed_4_posts, 0, 10)).andReturn(new ArrayList(list_4_posts));
+
+        // Test
+        replay(mockFeedsService);
+        List<? extends RestrictedPost> posts = aggFeedPosts.getPosts(aggFeed1, 0, 10, false);
+
+        // Check
+        verify(mockFeedsService);
+        assert posts.size() == 10;
+    }
+
+    @Test
+    public void testGetMoreThanAvailable() {
+        // Setup
+        expect(mockFeedsService.getPosts(feed_0_posts, 0, 2)).andReturn(new ArrayList(list_0_posts));
+        expect(mockFeedsService.getPosts(feed_1_posts, 0, 2)).andReturn(new ArrayList(list_1_posts));
+        expect(mockFeedsService.getPosts(feed_2_posts, 0, 2)).andReturn(new ArrayList(list_2_posts));
+        expect(mockFeedsService.getPosts(feed_3_posts, 0, 2)).andReturn(new ArrayList(list_2_posts));
+        expect(mockFeedsService.getPosts(feed_4_posts, 0, 2)).andReturn(new ArrayList(list_2_posts));
+
+        expect(mockFeedsService.getPosts(feed_2_posts, 2, 4)).andReturn(new ArrayList(list_0_posts));
+        expect(mockFeedsService.getPosts(feed_3_posts, 2, 4)).andReturn(new ArrayList(list_1_posts));
+        expect(mockFeedsService.getPosts(feed_4_posts, 2, 4)).andReturn(new ArrayList(list_2_posts));
+
+        expect(mockFeedsService.getPosts(feed_4_posts, 4, 6)).andReturn(new ArrayList(list_0_posts));
+
+        // Test
+        replay(mockFeedsService);
+        List<? extends RestrictedPost> posts = aggFeedPosts.getPosts(aggFeed2, 0, 2, false);
+
+        // Check
+        verify(mockFeedsService);
+        assert posts.size() == 0;
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/session/merge/test/GenericsExample1.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/merge/test/GenericsExample1.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/merge/test/GenericsExample1.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,35 @@
+package org.jboss.blog.session.merge.test;
+
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class GenericsExample1<T> {
+    public static <E> GenericsExample1<E> getInstance(E example) {
+        return new GenericsExample1<E>();
+    }
+
+    public void process(T example) { }
+
+    public static void test(List<? extends Number> list1, List<Long> list2) {
+        //GenericsExample1.getInstance(list1).process(list2);
+    }
+
+    public static void test2(List<Long> list2) {
+        new GenericsExample1<List<? extends Number>>().process(list2);
+    }
+    
+    public static void test2(List<? extends Number> list1, List<Long> list2) {
+        GenericsExample1<List<? extends Number>> x = new GenericsExample1<List<? extends Number>>();
+        //GenericsExample1<List<? extends Number>> y = getInstance(list1);
+    }
+
+    //
+
+    public void test3(List<? extends Number> list1) { }
+
+    public void test4(List<Long> list2) {
+        test3(list2);
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/session/merge/test/MergeServiceTest.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/merge/test/MergeServiceTest.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/merge/test/MergeServiceTest.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,281 @@
+package org.jboss.blog.session.merge.test;
+
+import static org.easymock.EasyMock.*;
+import org.easymock.EasyMock;
+import org.jboss.blog.model.feed.Feed;
+import org.jboss.blog.model.Post;
+import org.jboss.blog.model.Enclosure;
+import org.jboss.blog.model.Image;
+import org.jboss.blog.model.RestrictedPost;
+import org.jboss.blog.service.FeedsService;
+import org.jboss.blog.service.PostNotFoundException;
+import org.jboss.blog.session.merge.MergeServiceBean;
+import org.jboss.blog.tools.TestTools;
+import org.jboss.seam.log.Log;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MergeServiceTest {
+    private MergeServiceBean mergeServiceBean;
+
+    private Post post_2007_11_01_t1_c1_l1;
+    private Post post_2007_11_01_t2_c2_l2;
+    private Post post_2007_11_02_t3_c3_l3;
+    private Post post_2007_11_02_t1_c1_l4;
+    private Post post_2007_11_03_t4_c4_l5;
+    private Post post_2007_11_03_t5_c5_l6;
+    private Post post_2007_11_03_t6_c6_l6;
+    private Post post_2007_11_04_t6_c6_l7;
+    private Post post_2007_11_05_t6_c6_l8;
+
+    private EntityManager mockEntityManager;
+    private FeedsService mockFeedsService;
+
+    private Feed feed;
+
+    private Post createPost(Date published, String title, String content, String link) {
+        Post post = new Post();
+        post.setPublished(published);
+        post.setModified(published);
+        post.setTitle(title);
+        post.setContent(content);
+        post.setLink(link);
+        post.setEnclosures(new ArrayList<Enclosure>());
+        post.setImages(new ArrayList<Image>());
+
+        return post;
+    }
+
+    private Date createDate(int year, int month, int day) {
+        Calendar cal = Calendar.getInstance();
+        cal.set(year, month, day, 0, 0, 0);
+
+        return cal.getTime();
+    }
+
+    @BeforeClass
+    public void setupClass() {
+        mergeServiceBean = new MergeServiceBean();
+    }
+
+    @BeforeMethod
+    public void setupPosts() {        
+        post_2007_11_01_t1_c1_l1 = createPost(createDate(2007, 11, 1), "title1", "content1", "link1");
+        post_2007_11_01_t2_c2_l2 = createPost(createDate(2007, 11, 1), "title2", "content2", "link2");
+        post_2007_11_02_t3_c3_l3 = createPost(createDate(2007, 11, 2), "title3", "content3", "link3");
+        post_2007_11_02_t1_c1_l4 = createPost(createDate(2007, 11, 2), "title1", "content1", "link4");
+        post_2007_11_03_t4_c4_l5 = createPost(createDate(2007, 11, 3), "title4", "content4", "link5");
+        post_2007_11_03_t5_c5_l6 = createPost(createDate(2007, 11, 3), "title5", "content5", "link6");
+        post_2007_11_03_t6_c6_l6 = createPost(createDate(2007, 11, 3), "title6", "content6", "link6");
+        post_2007_11_04_t6_c6_l7 = createPost(createDate(2007, 11, 4), "title6", "content6", "link7");
+        post_2007_11_05_t6_c6_l8 = createPost(createDate(2007, 11, 5), "title6", "content6", "link8");
+    }
+
+    @BeforeMethod
+    public void setupMocks() throws IllegalAccessException, NoSuchFieldException {
+        mockEntityManager = createMock(EntityManager.class);
+        TestTools.setField(mergeServiceBean, "entityManager", mockEntityManager);
+
+        mockFeedsService = createMock(FeedsService.class);
+        TestTools.setField(mergeServiceBean, "feedsService", mockFeedsService);
+
+        TestTools.setField(mergeServiceBean, "titleAsIdService", new MockTitleAsIdService());
+
+        TestTools.setField(mergeServiceBean, "log", createMock(Log.class));
+
+        TestTools.setField(mergeServiceBean, "events", new MockEvents());
+
+        feed = new Feed();
+    }
+
+    @Test
+    public void testMergeIdentical() throws IllegalAccessException, NoSuchFieldException, PostNotFoundException {
+        List<Post> posts = Arrays.asList(post_2007_11_02_t3_c3_l3,
+                post_2007_11_01_t1_c1_l1);
+        
+        // Behaviour of mocks
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 0, 3))
+                .andReturn(posts);
+        expect(mockFeedsService.getPost(post_2007_11_02_t3_c3_l3.getTitle())).andReturn(post_2007_11_02_t3_c3_l3);
+        expect(mockFeedsService.getPost(post_2007_11_01_t1_c1_l1.getTitle())).andReturn(post_2007_11_01_t1_c1_l1);
+        mockEntityManager.flush();
+
+        // Test
+        replay(mockEntityManager);
+        replay(mockFeedsService);
+
+        mergeServiceBean.merge(feed, posts);
+        
+        verify(mockEntityManager);
+        verify(mockFeedsService);
+    }
+
+    @Test
+    public void testMergeToEmpty() throws IllegalAccessException, NoSuchFieldException {
+        List<Post> mergeTo = new ArrayList<Post>();
+        List<Post> mergeFrom = Arrays.asList(post_2007_11_02_t3_c3_l3,
+                post_2007_11_01_t1_c1_l1);
+
+        // Behaviour of mocks
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 0, 3)).andReturn(mergeTo);
+        mockEntityManager.persist(post_2007_11_02_t3_c3_l3);
+        mockEntityManager.flush();
+        mockEntityManager.persist(post_2007_11_01_t1_c1_l1);
+        mockEntityManager.flush();
+        mockEntityManager.flush();
+
+        // Test
+        replay(mockEntityManager);
+        replay(mockFeedsService);
+
+        mergeServiceBean.merge(feed, mergeFrom);
+
+        verify(mockEntityManager);
+        verify(mockFeedsService);
+    }
+
+    @Test
+    public void testMergeFromEmpty() throws IllegalAccessException, NoSuchFieldException {
+        List<Post> mergeTo = Arrays.asList(post_2007_11_02_t3_c3_l3,
+                post_2007_11_01_t1_c1_l1);
+        List<Post> mergeFrom = new ArrayList<Post>();
+
+        // Behaviour of mocks
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 0, 1)).andReturn(mergeTo);
+        mockEntityManager.flush();
+
+        // Test
+        replay(mockEntityManager);
+        replay(mockFeedsService);
+
+        mergeServiceBean.merge(feed, mergeFrom);
+
+        verify(mockEntityManager);
+        verify(mockFeedsService);
+    }
+
+    @Test
+    public void testMergeInterleaving_WithoutIntersection() throws IllegalAccessException, NoSuchFieldException {
+        List<Post> mergeTo = Arrays.asList(post_2007_11_05_t6_c6_l8,
+                post_2007_11_03_t4_c4_l5, post_2007_11_01_t1_c1_l1);
+        List<Post> mergeFrom = Arrays.asList(post_2007_11_04_t6_c6_l7,
+                post_2007_11_02_t1_c1_l4);
+
+        // Behaviour of mocks
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 0, 3)).andReturn(mergeTo);
+        mockEntityManager.persist(post_2007_11_04_t6_c6_l7);
+        mockEntityManager.flush();
+        mockEntityManager.persist(post_2007_11_02_t1_c1_l4);
+        mockEntityManager.flush();
+        mockEntityManager.flush();
+
+        // Test
+        replay(mockEntityManager);
+        replay(mockFeedsService);
+
+        mergeServiceBean.merge(feed, mergeFrom);
+
+        verify(mockEntityManager);
+        verify(mockFeedsService);
+    }
+
+    @Test
+    public void testMergeInterleaving_WithIntersection() throws IllegalAccessException, NoSuchFieldException, PostNotFoundException {
+        List<Post> mergeTo = Arrays.asList(post_2007_11_05_t6_c6_l8,
+                post_2007_11_04_t6_c6_l7, post_2007_11_02_t1_c1_l4,
+                post_2007_11_01_t1_c1_l1);
+        List<Post> mergeFrom = Arrays.asList(post_2007_11_04_t6_c6_l7,
+                post_2007_11_03_t5_c5_l6, post_2007_11_01_t2_c2_l2);
+
+        // Behaviour of mocks
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 0, 4)).andReturn(mergeTo);
+
+        expect(mockFeedsService.getPost(post_2007_11_04_t6_c6_l7.getTitle())).andReturn(post_2007_11_04_t6_c6_l7);
+
+        mockEntityManager.persist(post_2007_11_03_t5_c5_l6);
+        mockEntityManager.flush();
+
+        expect(mockFeedsService.getPost(post_2007_11_01_t1_c1_l1.getTitle())).andReturn(post_2007_11_01_t1_c1_l1);
+        mockEntityManager.flush();
+
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 4, 8)).andReturn(new ArrayList<Post>());
+
+        mockEntityManager.flush();
+
+        // Test
+        replay(mockEntityManager);
+        replay(mockFeedsService);
+
+        mergeServiceBean.merge(feed, mergeFrom);
+
+        assert "title2".equals(mergeTo.get(3).getTitle());
+        assert "content2".equals(mergeTo.get(3).getContent());
+        assert "link2".equals(mergeTo.get(3).getLink());
+
+        verify(mockEntityManager);
+        verify(mockFeedsService);
+    }
+
+    @Test
+    public void testMergeChangedPost() throws IllegalAccessException, NoSuchFieldException, PostNotFoundException {
+        List<Post> mergeTo = Arrays.asList(post_2007_11_05_t6_c6_l8,
+                post_2007_11_04_t6_c6_l7, post_2007_11_03_t5_c5_l6,
+                post_2007_11_03_t4_c4_l5, post_2007_11_02_t3_c3_l3);
+        List<Post> mergeFrom = Arrays.asList(post_2007_11_05_t6_c6_l8,
+                post_2007_11_04_t6_c6_l7, post_2007_11_03_t6_c6_l6,
+                post_2007_11_03_t4_c4_l5, post_2007_11_02_t3_c3_l3);
+
+        // Behaviour of mocks
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 0, 6)).andReturn(mergeTo);
+        expect(mockFeedsService.getPost(post_2007_11_05_t6_c6_l8.getTitle())).andReturn(post_2007_11_05_t6_c6_l8);
+        expect(mockFeedsService.getPost(post_2007_11_04_t6_c6_l7.getTitle())).andReturn(post_2007_11_04_t6_c6_l7);
+        expect(mockFeedsService.getPost(post_2007_11_03_t5_c5_l6.getTitle())).andReturn(post_2007_11_03_t5_c5_l6);
+        expect(mockFeedsService.getPost(post_2007_11_03_t4_c4_l5.getTitle())).andReturn(post_2007_11_03_t4_c4_l5);
+        expect(mockFeedsService.getPost(post_2007_11_02_t3_c3_l3.getTitle())).andReturn(post_2007_11_02_t3_c3_l3);
+        mockEntityManager.flush();
+        mockEntityManager.flush();
+
+        // Test
+        replay(mockEntityManager);
+        replay(mockFeedsService);
+
+        mergeServiceBean.merge(feed, mergeFrom);
+        assert "title6".equals(mergeTo.get(2).getTitle());
+        assert "content6".equals(mergeTo.get(2).getContent());        
+
+        verify(mockEntityManager);
+        verify(mockFeedsService);
+    }
+
+    @Test
+    public void testMergeOld() throws IllegalAccessException, NoSuchFieldException {
+        List<Post> mergeTo1 = Arrays.asList(post_2007_11_05_t6_c6_l8, post_2007_11_04_t6_c6_l7);
+        List<Post> mergeTo2 = Arrays.asList(post_2007_11_03_t5_c5_l6, post_2007_11_03_t4_c4_l5);
+        List<Post> mergeTo3 = Arrays.asList(post_2007_11_01_t1_c1_l1);
+        List<Post> mergeFrom = Arrays.asList(post_2007_11_02_t1_c1_l4);
+
+        // Behaviour of mocks
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 0, 2)).andReturn(mergeTo1);
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 2, 4)).andReturn(mergeTo2);
+        EasyMock.<List<? extends RestrictedPost>>expect(mockFeedsService.getPosts(feed, 4, 6)).andReturn(mergeTo3);
+        mockEntityManager.persist(post_2007_11_02_t1_c1_l4);
+        mockEntityManager.flush();
+        mockEntityManager.flush();
+
+        // Test
+        replay(mockEntityManager);
+        replay(mockFeedsService);
+
+        mergeServiceBean.merge(feed, mergeFrom);
+
+        verify(mockEntityManager);
+        verify(mockFeedsService);
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/session/merge/test/MockEvents.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/merge/test/MockEvents.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/merge/test/MockEvents.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,12 @@
+package org.jboss.blog.session.merge.test;
+
+import org.jboss.seam.core.Events;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MockEvents extends Events {
+    public void raiseEvent(String s, Object... objects) {
+
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/session/merge/test/MockTitleAsIdService.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/merge/test/MockTitleAsIdService.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/merge/test/MockTitleAsIdService.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,13 @@
+package org.jboss.blog.session.merge.test;
+
+import org.jboss.blog.session.merge.TitleAsIdService;
+import org.jboss.blog.tools.StringTools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class MockTitleAsIdService implements TitleAsIdService {
+    public String generateTitleAsId(String title) {
+        return StringTools.convertTitleToLink(title);
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,29 @@
+package org.jboss.blog.session.parser;
+
+import com.sun.syndication.io.SyndFeedInput;
+import com.sun.syndication.io.XmlReader;
+import com.sun.syndication.io.FeedException;
+import com.sun.syndication.feed.synd.SyndFeed;
+import com.sun.syndication.feed.synd.SyndEntry;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RomeExample {
+    public static void main(String[] args) throws IOException, FeedException {
+        SyndFeedInput input = new SyndFeedInput();
+        SyndFeed syndFeed = input.build(new XmlReader(new URL("http://labs.jboss.com/feeds/jbosslabs/podcast/rss2")));
+
+        List entries = syndFeed.getEntries();
+
+        SyndEntry entry = (SyndEntry) entries.get(0);
+        List enclosures = entry.getEnclosures();
+
+        System.out.println(syndFeed.getTitle());
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample2.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample2.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample2.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,29 @@
+package org.jboss.blog.session.parser;
+
+import com.sun.syndication.io.SyndFeedInput;
+import com.sun.syndication.io.XmlReader;
+import com.sun.syndication.io.FeedException;
+import com.sun.syndication.feed.synd.SyndFeed;
+import com.sun.syndication.feed.synd.SyndEntry;
+
+import java.net.URL;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RomeExample2 {
+    public static void main(String[] args) throws IOException, FeedException {
+        SyndFeedInput input = new SyndFeedInput();
+        SyndFeed syndFeed = input.build(
+                new XmlReader(new URL("http://rss.cnn.com/services/podcasting/newscast/rss.xml")));
+
+        List entries = syndFeed.getEntries();
+
+        SyndEntry entry = (SyndEntry) entries.get(0);
+        List x = (List) entry.getForeignMarkup();
+
+        System.out.println(x.get(0).getClass().getName());
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample3.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample3.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/session/parser/RomeExample3.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,31 @@
+package org.jboss.blog.session.parser;
+
+import com.sun.syndication.io.SyndFeedInput;
+import com.sun.syndication.io.XmlReader;
+import com.sun.syndication.io.FeedException;
+import com.sun.syndication.feed.synd.SyndFeed;
+import com.sun.syndication.feed.synd.SyndEntry;
+import com.sun.syndication.feed.module.Module;
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RomeExample3 {
+    public static void main(String[] args) throws IOException, FeedException, ParserException {
+        /*SyndFeedInput input = new SyndFeedInput();
+
+        URLConnection conn = new URL("http://www.warski.org/blog/?feed=rss2").openConnection();
+        conn.setReadTimeout(3000);
+        conn.setConnectTimeout(5000);
+        conn.connect();
+
+        SyndFeed syndFeed = input.build(new XmlReader(conn.getInputStream()));*/
+
+        new ParserServiceImpl().parse("http://www.warski.org/blog/?feed=rss2");
+    }
+}
\ No newline at end of file

Added: feeds100P26/src/test/org/jboss/blog/tools/FixHtmlExample1.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/tools/FixHtmlExample1.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/tools/FixHtmlExample1.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,10 @@
+package org.jboss.blog.tools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FixHtmlExample1 {
+    public static void main(String[] args) {
+        System.out.println(StringTools.fixHtml("Gurkan Erdogdu and Kris Verlaenen a warm welcome in our <a href=\"http://jboss.com/index.html?module=bb&op=viewforum&f=201\">forums</a>!"));
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/tools/FixHtmlTest.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/tools/FixHtmlTest.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/tools/FixHtmlTest.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,26 @@
+package org.jboss.blog.tools;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FixHtmlTest {
+    @DataProvider(name = "texts")
+    public Object[][] getData() {
+        return new Object[][] {
+                { "<p>Some text.</p>", "<p>Some text.</p>\n" },
+                { "<p><i>Some text.</i></p>", "<p><i>Some text.</i>\n</p>\n" },
+                { "Some text.", "Some text." },
+                { "<p>Some text.", "<p>Some text.</p>\n" },
+                { "Some text.</p>", "Some text." }
+        };
+    }
+
+    @Test(dataProvider = "texts")
+    public void test(String original, String fixed) {
+        System.out.println(StringTools.fixHtml(original));
+        assert fixed.equals(StringTools.fixHtml(original));
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/tools/StripHtmlTest.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/tools/StripHtmlTest.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/tools/StripHtmlTest.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,25 @@
+package org.jboss.blog.tools;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class StripHtmlTest {
+    @DataProvider(name = "texts")
+    public Object[][] getData() {
+        return new Object[][] {
+                { "<p>Some text.</p>", "Some text." },
+                { "This: < shouldn't get removed.", "This: < shouldn't get removed." },
+                { "<p>Non-terminated tags. <a> And now this!. >", "Non-terminated tags. And now this!. >" },
+                { "<p > Tags with spaces, and some <a > not terminated </a>.",
+                        "Tags with spaces, and some not terminated ." }
+        };
+    }
+
+    @Test(dataProvider = "texts")
+    public void test(String original, String stripped) {
+        assert stripped.equals(StringTools.stripHtml(original));
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/tools/TestTools.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/tools/TestTools.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/tools/TestTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,15 @@
+package org.jboss.blog.tools;
+
+import java.lang.reflect.Field;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TestTools {
+    public static void setField(Object o, String fieldName, Object setTo)
+            throws NoSuchFieldException, IllegalAccessException {
+        Field f = o.getClass().getDeclaredField(fieldName);
+        f.setAccessible(true);
+        f.set(o, setTo);
+    }
+}

Added: feeds100P26/src/test/org/jboss/blog/tools/TitleToLinkTest.java
===================================================================
--- feeds100P26/src/test/org/jboss/blog/tools/TitleToLinkTest.java	                        (rev 0)
+++ feeds100P26/src/test/org/jboss/blog/tools/TitleToLinkTest.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,33 @@
+package org.jboss.blog.tools;
+
+import org.testng.annotations.Test;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class TitleToLinkTest {
+    @Test
+    public void testEmpty() {
+        assert "".equals(StringTools.convertTitleToLink(""));
+    }
+
+    @Test
+    public void testEmpty2() {
+        assert "".equals(StringTools.convertTitleToLink("!@#$%"));
+    }
+
+    @Test
+    public void testNormal() {
+        assert "abd_ef_s_jj".equals(StringTools.convertTitleToLink("*abd**ef***s*jj*"));
+    }
+
+    @Test
+    public void testEnd() {
+        assert "aaa".equals(StringTools.convertTitleToLink("***aaa"));
+    }
+
+    @Test
+    public void testBeginning() {
+        assert "aaa".equals(StringTools.convertTitleToLink("aaa***"));
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/GeneralTools.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/GeneralTools.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/GeneralTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,185 @@
+package org.jboss.blog.tools;
+
+import java.io.*;
+import java.util.List;
+import java.util.Collections;
+import java.util.Date;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class GeneralTools {
+    public static boolean objectsEqual(Object o1, Object o2) {
+        if (o1 == null) {
+            return o2 == null;
+        } else {
+            return o1.equals(o2);
+        }
+    }
+
+    private static final int TRANSFER_BUFFER_SIZE = 2048;
+
+    /**
+     * Transferes all bytes from the given input stream to the given output
+     * stream.
+     *
+     * @param is
+     *            Input stream to read from.
+     * @param w
+     *            Printwriter to write to.
+     * @throws java.io.IOException In case of an IO exception.
+     */
+    public static void transfer(InputStream is, Writer w) throws IOException {
+        char[] buffer = new char[TRANSFER_BUFFER_SIZE];
+        int read;
+        InputStreamReader isr = new InputStreamReader(is);
+        while ((read = isr.read(buffer)) != -1) {
+            w.write(buffer, 0, read);
+        }
+    }
+
+    /**
+     * Transferes all bytes from the given reader to the writer
+     *
+     * @param r
+     *            Reader to read from.
+     * @param w
+     *            Writer to write to.
+     * @throws java.io.IOException In case of an IO exception.
+     */
+    public static void transfer(Reader r, Writer w) throws IOException {
+        char[] buffer = new char[TRANSFER_BUFFER_SIZE];
+        int read;
+        while ((read = r.read(buffer)) != -1) {
+            w.write(buffer, 0, read);
+        }
+    }
+
+    /**
+     * Transferes all bytes from the given input stream to the given output
+     * stream.
+     *
+     * @param is
+     *            Input stream to read from.
+     * @param os
+     *            Output stream to write to.
+     * @throws IOException In case of an IO exception.
+     */
+    public static void transfer(InputStream is, OutputStream os) throws IOException {
+        byte[] buffer = new byte[TRANSFER_BUFFER_SIZE];
+        int read;
+        while ((read = is.read(buffer)) != -1) {
+            os.write(buffer, 0, read);
+        }
+    }
+
+    public static <T> int safeCompare(Comparable<T> o1, T o2) {
+        if (o1 == null) {
+            if (o2 == null) {
+                return 0;
+            }
+
+            return 1;
+        } else if (o2 == null) {
+            return -1;
+        } else {
+            return o1.compareTo(o2);
+        }
+    }
+
+    public static <T> List<T> subList(List<T> list, int from, int to) {
+        if (from > to) {
+            return Collections.emptyList();
+        }
+
+        if (from > list.size()) {
+            return Collections.emptyList();
+        }
+
+        return list.subList(from, Math.min(to, list.size()));
+    }
+
+    public static <T> void moveElement(List<T> list, int from, int to) {
+        if (from < 0 || from > list.size() || to < 0 || to > list.size()) {
+            return;
+        }
+
+        if (from == to) {
+            return;
+        }
+
+        T toMove = list.get(from);
+
+        if (from < to) {
+            int currentIndex = from;
+            while (currentIndex != to) {
+                list.set(currentIndex, list.get(currentIndex + 1));
+                currentIndex++;
+            }
+
+            list.set(to, toMove);
+        } else {
+            int currentIndex = from;
+            while (currentIndex != to) {
+                list.set(currentIndex, list.get(currentIndex - 1));
+                currentIndex--;
+            }
+
+            list.set(to, toMove);
+        }
+    }
+
+    public static int compareDates(Date date1, Date date2) {
+        long seconds1 = (date1 == null) ? 0 : (date1.getTime() / 1000);
+        long seconds2 = (date2 == null) ? 0 : (date2.getTime() / 1000);
+
+        if (seconds1 == seconds2) {
+            return 0;
+        } else if (seconds1 > seconds2) {
+            return 1;
+        } else {
+            return -1;
+        }
+    }
+
+    public static int compareStrings(String str1, String str2) {
+        if (str1 == null) {
+            if (str2 == null) {
+                return 0;
+            } else {
+                return -1;
+            }
+        } else if (str2 == null) {
+            return 1;
+        } else {
+            return str1.compareTo(str2);
+        }
+    }
+
+    public static String readInputStream(InputStream is) throws IOException {
+        StringBuffer contents = new StringBuffer();
+
+        BufferedReader input =  new BufferedReader(new InputStreamReader(is));
+        try {
+            String line;
+            while ((line = input.readLine()) != null) {
+                contents.append(line);
+                contents.append(System.getProperty("line.separator"));
+            }
+        } finally {
+            input.close();
+        }
+
+        return contents.toString();
+    }
+
+    public static String getExceptionStackTrace(Exception e) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintWriter pw = new PrintWriter(baos);
+
+        e.printStackTrace(pw);
+
+        pw.flush();
+        return baos.toString();
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/KeyNotMappedException.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/KeyNotMappedException.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/KeyNotMappedException.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+package org.jboss.blog.tools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class KeyNotMappedException extends RuntimeException {
+    public KeyNotMappedException() {
+    }
+
+    public KeyNotMappedException(String message) {
+        super(message);
+    }
+
+    public KeyNotMappedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public KeyNotMappedException(Throwable cause) {
+        super(cause);
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/KeySafeMap.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/KeySafeMap.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/KeySafeMap.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,84 @@
+package org.jboss.blog.tools;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Collection;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class KeySafeMap<K, V> implements Map<K, V> {
+    private Map<K, V> delegate;
+
+    public KeySafeMap(Map<K, V> delegate) {
+        if (delegate == null) {
+            throw new IllegalArgumentException("Delegate map cannot be null!");
+        }
+
+        this.delegate = delegate;
+    }
+
+    public static <K, V> KeySafeMap<K, V> wrap(Map<K, V> toWrap) {
+        return new KeySafeMap<K, V>(toWrap);
+    }
+
+    public int size() {
+        return delegate.size();
+    }
+
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    public boolean containsKey(Object key) {
+        return delegate.containsKey(key);
+    }
+
+    public boolean containsValue(Object value) {
+        return delegate.containsValue(value);
+    }
+
+    public V get(Object key) {
+        if (containsKey(key)) {
+            return delegate.get(key);
+        } else {
+            throw new KeyNotMappedException(StringTools.safeToString(key));
+        }
+    }
+
+    public V put(K key, V value) {
+        return delegate.put(key, value);
+    }
+
+    public V remove(Object key) {
+        return delegate.remove(key);
+    }
+
+    public void putAll(Map<? extends K, ? extends V> t) {
+        delegate.putAll(t);
+    }
+
+    public void clear() {
+        delegate.clear();
+    }
+
+    public Set<K> keySet() {
+        return delegate.keySet();
+    }
+
+    public Collection<V> values() {
+        return delegate.values();
+    }
+
+    public Set<Entry<K, V>> entrySet() {
+        return delegate.entrySet();
+    }
+
+    public boolean equals(Object o) {
+        return delegate.equals(o);
+    }
+
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/Pair.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/Pair.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/Pair.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,34 @@
+package org.jboss.blog.tools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class Pair<T1, T2> {
+    private T1 first;
+    private T2 second;
+
+    public Pair(T1 first, T2 second) {
+        this.first = first;
+        this.second = second;
+    }
+
+    public T1 getFirst() {
+        return first;
+    }
+
+    public void setFirst(T1 first) {
+        this.first = first;
+    }
+
+    public T2 getSecond() {
+        return second;
+    }
+
+    public void setSecond(T2 second) {
+        this.second = second;
+    }
+
+    public static <T1, T2> Pair<T1,T2> createPair(T1 first, T2 second) {
+        return new Pair<T1, T2>(first, second);
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/StringTools.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/StringTools.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/StringTools.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,237 @@
+package org.jboss.blog.tools;
+
+import au.id.jericho.lib.html.Source;
+import org.htmlcleaner.HtmlCleaner;
+import org.xml.sax.SAXException;
+import org.xml.sax.InputSource;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXParseException;
+import org.jboss.seam.log.Logging;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.StringReader;
+import java.text.StringCharacterIterator;
+import java.text.CharacterIterator;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class StringTools {
+    public static boolean isEmpty(String s) {
+        return s == null || "".equals(s);
+    }
+
+    public static String safeToString(Object o) {
+        return o == null ? null : o.toString();
+    }
+
+    public static String convertTitleToLink(String title) {
+        if (title == null) {
+            return null;
+        }
+
+        char[] titleWithUnderscores = title.toLowerCase().replaceAll("[^a-z0-9_]", "_").toCharArray();
+
+        StringBuffer newTitle = new StringBuffer();
+
+        // Removing _ from the beginning.
+        int titleIndex = 0;
+        while ((titleIndex < titleWithUnderscores.length) && (titleWithUnderscores[titleIndex] == '_')) {
+            titleIndex++;
+        }
+
+        // Removing multiple _ in the text.
+        boolean previousLetter = true;
+        while (titleIndex < titleWithUnderscores.length) {
+            if (titleWithUnderscores[titleIndex] == '_') {
+                if (previousLetter) {
+                    newTitle.append(titleWithUnderscores[titleIndex]);
+                }
+
+                previousLetter = false;
+            } else {
+                newTitle.append(titleWithUnderscores[titleIndex]);
+                previousLetter = true;
+            }
+
+            titleIndex++;
+        }
+
+        // Removing _ from the end, if there was one.
+        if ((newTitle.length() > 0) && (newTitle.charAt(newTitle.length()-1) == '_')) {
+            newTitle.deleteCharAt(newTitle.length()-1);
+        }
+
+        return newTitle.toString();
+    }
+
+    public static String stripHtml(String html) {
+        Source source = new Source(html);
+
+        source.fullSequentialParse();
+
+        return source.getTextExtractor().toString();
+    }
+
+    public static boolean isValidXml(String html) {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setValidating(false);
+
+        DocumentBuilder builder;
+        try {
+            builder = factory.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            Logging.getLog(StringTools.class).error(e);
+            return false;
+        }
+
+        builder.setErrorHandler(new ErrorHandler() {
+            public void warning(SAXParseException exception) throws SAXException { throw exception; }
+            public void error(SAXParseException exception) throws SAXException { throw exception; }
+            public void fatalError(SAXParseException exception) throws SAXException { throw exception; }
+        });
+
+        try {
+            builder.parse(new InputSource(new StringReader(html)));
+            return true;
+        } catch (SAXException e) {
+            return false;
+        } catch (IOException e) {
+            Logging.getLog(StringTools.class).error(e);
+            return false;
+        }
+    }
+
+    public static String fixHtml(String html) {
+        if (isEmpty(html)) {
+            return html;
+        }
+
+        String htmlToCheck = "<div>" + html  + "</div>";
+
+        if (isValidXml(htmlToCheck)) {
+            return html;
+        }
+
+        HtmlCleaner cleaner = new HtmlCleaner(htmlToCheck);
+        try {
+            cleaner.setOmitHtmlEnvelope(true);
+            cleaner.setOmitXmlDeclaration(true);
+            cleaner.setRecognizeUnicodeChars(false);
+            cleaner.setTranslateSpecialEntities(false);
+            cleaner.setAdvancedXmlEscape(true);
+            cleaner.clean();
+
+            String ret = cleaner.getXmlAsString().trim();
+
+            // Removing the <div> and </div>
+            ret = ret.substring(5);
+            ret = ret.substring(0, ret.length()-6);
+
+            return ret;
+        } catch (IOException e) {
+            return html;
+        }
+    }
+
+    public static String createSummary(String s, int length) {
+        if (s == null) {
+            return null;
+        }
+
+        s = stripHtml(s);
+
+        if (s.length() > length) {
+            s = s.substring(0, length);
+            return s + "...";
+        } else {
+            return s;
+        }
+    }
+
+    /**
+     * Escape characters for text appearing in HTML markup.
+     *
+     * <P>This method exists as a defence against Cross Site Scripting (XSS) hacks.
+     * This method escapes all characters recommended by the Open Web App
+     * Security Project -
+     * <a href='http://www.owasp.org/index.php/Cross_Site_Scripting'>link</a>.
+     *
+     * <P>The following characters are replaced with corresponding HTML
+     * character entities :
+     * <table border='1' cellpadding='3' cellspacing='0'>
+     * <tr><th> Character </th><th> Encoding </th></tr>
+     * <tr><td> < </td><td> &lt; </td></tr>
+     * <tr><td> > </td><td> &gt; </td></tr>
+     * <tr><td> & </td><td> &amp; </td></tr>
+     * <tr><td> " </td><td> &quot;</td></tr>
+     * <tr><td> ' </td><td> &#039;</td></tr>
+     * <tr><td> ( </td><td> &#040;</td></tr>
+     * <tr><td> ) </td><td> &#041;</td></tr>
+     * <tr><td> # </td><td> &#035;</td></tr>
+     * <tr><td> % </td><td> &#037;</td></tr>
+     * <tr><td> ; </td><td> &#059;</td></tr>
+     * <tr><td> + </td><td> &#043; </td></tr>
+     * <tr><td> - </td><td> &#045; </td></tr>
+     * </table>
+     *
+     * <P>Note that JSTL's {@code <c:out>} escapes <em>only the first
+     * five</em> of the above characters.
+     *
+     * @author http://www.javapractices.com/topic/TopicAction.do;jsessionid=A293F4E3A21E476DC8488FED3DE883A0?Id=96
+     */
+    public static String escape(String toEscape){
+        final StringBuilder result = new StringBuilder();
+        final StringCharacterIterator iterator = new StringCharacterIterator(toEscape);
+        char character =  iterator.current();
+        while (character != CharacterIterator.DONE ){
+            if (character == '<') {
+                result.append("&lt;");
+            }
+            else if (character == '>') {
+                result.append("&gt;");
+            }
+            else if (character == '&') {
+                result.append("&amp;");
+            }
+            else if (character == '\"') {
+                result.append("&quot;");
+            }
+            else if (character == '\'') {
+                result.append("&#039;");
+            }
+            else if (character == '(') {
+                result.append("&#040;");
+            }
+            else if (character == ')') {
+                result.append("&#041;");
+            }
+            else if (character == '#') {
+                result.append("&#035;");
+            }
+            else if (character == '%') {
+                result.append("&#037;");
+            }
+            else if (character == ';') {
+                result.append("&#059;");
+            }
+            else if (character == '+') {
+                result.append("&#043;");
+            }
+            else if (character == '-') {
+                result.append("&#045;");
+            }
+            else {
+                //the char is not a special one
+                //add it to the result as is
+                result.append(character);
+            }
+            character = iterator.next();
+        }
+        return result.toString();
+    }
+
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/search/UnrestrictedFeedFilter.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/search/UnrestrictedFeedFilter.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/search/UnrestrictedFeedFilter.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,25 @@
+package org.jboss.blog.tools.search;
+
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.TermDocs;
+import org.apache.lucene.index.Term;
+
+import java.util.BitSet;
+import java.io.IOException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class UnrestrictedFeedFilter extends Filter {
+    public BitSet bits(IndexReader indexReader) throws IOException {
+        BitSet bitSet = new BitSet(indexReader.maxDoc());
+        TermDocs termDocs = indexReader.termDocs(new Term("restricted", "0"));
+        
+        while (termDocs.next()) {
+            bitSet.set(termDocs.doc());
+        }
+
+        return bitSet;
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/FeedBridge.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/FeedBridge.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/FeedBridge.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,22 @@
+package org.jboss.blog.tools.search.bridge;
+
+import org.hibernate.search.bridge.StringBridge;
+import org.jboss.blog.model.feed.Feed;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FeedBridge implements StringBridge {
+    public String objectToString(Object object) {
+        if (object == null) {
+            return null;
+        }
+
+        Feed feed = (Feed) object;
+        if (feed.isFeedRestricted()) {
+            return "1";
+        } else {
+            return "0";
+        }
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/StripHtmlBridge.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/StripHtmlBridge.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/search/bridge/StripHtmlBridge.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,17 @@
+package org.jboss.blog.tools.search.bridge;
+
+import org.hibernate.search.bridge.StringBridge;
+import org.jboss.blog.tools.StringTools;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class StripHtmlBridge implements StringBridge {
+    public String objectToString(Object o) {
+        if (o == null) {
+            return null;
+        }
+
+        return StringTools.stripHtml(o.toString());
+    }
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/validator/Regexp.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/validator/Regexp.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/validator/Regexp.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,16 @@
+package org.jboss.blog.tools.validator;
+
+import org.hibernate.validator.ValidatorClass;
+
+import java.lang.annotation.*;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+ at ValidatorClass(RegexpValidator.class)
+ at Target({ElementType.FIELD, ElementType.METHOD})
+ at Retention(RetentionPolicy.RUNTIME)
+ at Documented
+public @interface Regexp {
+    String message() default "must be a valid regular expression";
+}

Added: feeds100P26/src/tools/org/jboss/blog/tools/validator/RegexpValidator.java
===================================================================
--- feeds100P26/src/tools/org/jboss/blog/tools/validator/RegexpValidator.java	                        (rev 0)
+++ feeds100P26/src/tools/org/jboss/blog/tools/validator/RegexpValidator.java	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,36 @@
+package org.jboss.blog.tools.validator;
+
+import org.hibernate.validator.PropertyConstraint;
+import org.hibernate.validator.Validator;
+import org.hibernate.mapping.Property;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class RegexpValidator implements Validator<Regexp>, PropertyConstraint {
+    public void initialize(Regexp parameters) { }
+
+    public boolean isValid(Object value) {
+        if (value == null) {
+            return true;
+        }
+
+        if (!(value instanceof String)) {
+            return false;
+        }
+
+        String string = (String) value;
+
+        try {
+            Pattern.compile(string);
+            return true;
+        } catch (PatternSyntaxException e) {
+            return false;
+        }
+    }
+
+    public void apply(Property property) { }
+}
\ No newline at end of file

Added: feeds100P26/view/common/ajax_status.xhtml
===================================================================
--- feeds100P26/view/common/ajax_status.xhtml	                        (rev 0)
+++ feeds100P26/view/common/ajax_status.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,17 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+    <a:status stopStyleClass="ajaxStatus" startStyleClass="ajaxStatus">
+        <f:facet name="start">
+            <img src="/feeds/images/wait.gif" alt="" /> Please wait ...
+        </f:facet>
+        <f:facet name="stop">
+        </f:facet>
+    </a:status>
+</ui:composition>
\ No newline at end of file

Added: feeds100P26/view/common/next_previous_navigation.xhtml
===================================================================
--- feeds100P26/view/common/next_previous_navigation.xhtml	                        (rev 0)
+++ feeds100P26/view/common/next_previous_navigation.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,29 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+    <s:fragment rendered="#{navigationBean.showPrevious}">
+        <li>
+            <s:link view="#{viewId}" value="&#171; Previous"  propagation="none">
+                <f:param name="from" value="#{navigationBean.previousFrom}" />
+            </s:link>
+        </li>
+    </s:fragment>
+
+    <s:fragment rendered="#{navigationBean.showNext}">
+        <li>
+            <s:link view="#{viewId}" value="Next &#187;" rendered="#{navigationBean.showNext}" propagation="none">
+                <f:param name="from" value="#{navigationBean.nextFrom}" />
+            </s:link>
+        </li>
+    </s:fragment>
+
+    <s:fragment rendered="#{(navigationBean.showNext || navigationBean.showPrevious) and showColon}">
+        <li>:</li>
+    </s:fragment>
+</ui:composition>
\ No newline at end of file

Added: feeds100P26/view/common/post.xhtml
===================================================================
--- feeds100P26/view/common/post.xhtml	                        (rev 0)
+++ feeds100P26/view/common/post.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,61 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+    <h3>
+        <s:link value="#{post.title}" view="/view/post.xhtml" propagation="none">
+            <f:param name="post" value="#{post.titleAsId}" />
+        </s:link>
+    </h3>
+
+    <p class="blogauthortag">
+        Posted on #{post.published} by <b>#{post.effectiveAuthor}</b> #{additionalHeader}
+        [ <a href="#{post.link}">View original post</a> ]
+        <s:fragment rendered="#{showAddToHighlights and identity.hasPermission('management', 'view') and highlightsSecurity.feeds.size() > 0}">
+            [&#160;<s:link value="Add this post to a highlights feed" view="/manage/highlights/post_add.xhtml">
+            <f:param name="post" value="#{post.titleAsId}" />
+        </s:link>&#160;]
+        </s:fragment>
+    </p>
+
+    <ui:repeat var="image" value="#{post.images}">
+        <img src="#{image.url}" alt="#{image.title}" width="180" />
+    </ui:repeat>
+
+    <ui:repeat var="enclosure" value="#{post.enclosures}">
+        <p class="blogauthortag">
+            Enclosure: <h:outputLink value="#{enclosure.url}">#{enclosure.url}</h:outputLink>
+        </p>
+    </ui:repeat>
+
+    <h:outputText value="#{post.content}" escape="false" rendered="#{!showSummary}" />
+    <h:outputText value="#{stringTools.createSummary(post.content)}" rendered="#{showSummary}" />
+
+    <s:fragment rendered="#{showPostTo and post.feed.showDzone}">
+        <img src="http://www.dzone.com/links/themes/reader/images/actions/icon-vote.gif" alt="" />
+        <a href="http://www.dzone.com/links/add.html?url=#{postToTools.encodeLink(post)}&amp;title=#{postToTools.encodeTitleForDelicious(post)}">
+            Post to DZone</a> &#160;
+    </s:fragment>
+    <s:fragment rendered="#{showPostTo and post.feed.showDelicious}">
+        <img src="http://images.del.icio.us/static/img/delicious.small.gif" alt="" />
+        <a href="http://del.icio.us/post?v=4&amp;url=#{postToTools.encodeLink(post)}&amp;title=#{postToTools.encodeTitleForDelicious(post)}">
+            Post to del.icio.us</a> &#160;
+    </s:fragment>
+    <s:fragment rendered="#{showPostTo and post.feed.showDigg}">
+        <img src="http://digg.com/img/badges/10x10-digg-thumb.png" alt="" />
+        <a href="http://digg.com/submit?url=#{postToTools.encodeLink(post)}&amp;title=#{postToTools.encodeTitleForDigg(post)}&amp;bodytext=#{postToTools.encodeBodyForDigg(post)}&amp;media=news&amp;topic=programming">
+            Digg this!</a> &#160;
+    </s:fragment>
+    <s:fragment rendered="#{showPostTo and post.feed.showStumble}">
+        <img border="0" src="http://cdn.stumble-upon.com/images/16x16_su_3d.gif" width="10" height="10" alt="" />
+        <a href="http://www.stumbleupon.com/submit?url=#{postToTools.encodeLink(post)}&amp;title=#{postToTools.encodeTitleForDelicious(post)}">
+            Stumble It!</a>
+    </s:fragment>
+
+    <hr />
+</ui:composition>
\ No newline at end of file

Added: feeds100P26/view/emails/new_proposition_email.xhtml
===================================================================
--- feeds100P26/view/emails/new_proposition_email.xhtml	                        (rev 0)
+++ feeds100P26/view/emails/new_proposition_email.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<m:message xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:m="http://jboss.com/products/seam/mail"
+    xmlns:h="http://java.sun.com/jsf/html"
+    charset="UTF-8">
+
+    <m:from name="JBoss.ORG Feeds robot" address="feeds-robot at jboss.org" />
+    <m:to>#{propositionsListener.currentEmail}</m:to>
+    <m:subject>A new feed has been proposed</m:subject>
+
+    <m:body type="plain">
+        A new feed has been proposed:<br />
+        <br />
+        Title: #{feedMod.feed.title}<br />
+        Link: #{feedMod.feed.remoteLink}<br />
+        <br />
+        Visit #{linkService.serverAddress}/#{linkService.contextName}/manage/proposition/proposition_list.seam to accept/reject the feed.<br />
+        <br />
+        -- <br />
+        JBoss.ORG Feeds robot
+    </m:body>
+</m:message>
\ No newline at end of file

Added: feeds100P26/view/emails/proposition_accepted.xhtml
===================================================================
--- feeds100P26/view/emails/proposition_accepted.xhtml	                        (rev 0)
+++ feeds100P26/view/emails/proposition_accepted.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<m:message xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:m="http://jboss.com/products/seam/mail"
+    xmlns:h="http://java.sun.com/jsf/html"
+    charset="UTF-8">
+
+    <m:from name="JBoss.ORG Feeds robot" address="feeds-robot at jboss.org" />
+    <m:to>#{propositionsListener.currentEmail}</m:to>
+    <m:subject>Your feed has been accepted!</m:subject>
+
+    <m:body type="plain">
+        Welcome!<br />
+        <br />
+        Congratulations! Your feed: '#{feedMod.feed.title}' has been accepted and will now be aggregated on
+        JBoss.ORG Feeds.<br />
+        <br />
+        To view the feed, enter:<br />
+        #{linkService.generateFeedPageLink(feedMod.feed)}<br />
+        <br />
+        And don't forget to visit http://labs.jboss.com often!<br />
+        <br />
+        -- <br />
+        JBoss.ORG Feeds robot
+    </m:body>
+</m:message>
\ No newline at end of file

Added: feeds100P26/view/emails/proposition_rejected.xhtml
===================================================================
--- feeds100P26/view/emails/proposition_rejected.xhtml	                        (rev 0)
+++ feeds100P26/view/emails/proposition_rejected.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<m:message xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:m="http://jboss.com/products/seam/mail"
+    xmlns:h="http://java.sun.com/jsf/html"
+    charset="UTF-8">
+
+    <m:from name="JBoss.ORG Feeds robot" address="feeds-robot at jboss.org" />
+    <m:to>#{propositionsListener.currentEmail}</m:to>
+    <m:subject>Your feed has been rejected</m:subject>
+
+    <m:body type="plain">
+        Unfortunately, your feed '#{feedMod.feed.title}' has been rejected.<br />
+        <br />
+        Is it a blog on a JBoss-related subject?<br />
+        Did you submit a feed containing only JBoss-related posts?<br />
+        <br />
+        If so, and you still think that your feed should be aggregated, please contact the project to which's group you
+        submitted your blog.
+        <br />
+        -- <br />
+        JBoss.ORG Feeds robot
+    </m:body>
+</m:message>
\ No newline at end of file

Added: feeds100P26/view/emails/test_email.xhtml
===================================================================
--- feeds100P26/view/emails/test_email.xhtml	                        (rev 0)
+++ feeds100P26/view/emails/test_email.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<m:message xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:m="http://jboss.com/products/seam/mail"
+    xmlns:h="http://java.sun.com/jsf/html"
+    charset="UTF-8">
+
+    <m:from name="JBoss.ORG Feeds robot" address="feeds-robot at jboss.org" />
+    <m:to>#{configurationManager.configuration.adminEmail}</m:to>
+    <m:subject>JBoss Labs Feeds test e-mail</m:subject>
+
+    <m:body type="plain">
+        This is a test e-mail from JBoss Labs Feeds.<br />
+        <br />
+        -- <br />
+        JBoss.ORG Feeds robot
+    </m:body>
+</m:message>
\ No newline at end of file

Added: feeds100P26/view/error/error.xhtml
===================================================================
--- feeds100P26/view/error/error.xhtml	                        (rev 0)
+++ feeds100P26/view/error/error.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,16 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Error
+    </ui:define>
+
+    <ui:define name="body">
+        <p>Something bad happened :-(</p>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/error/feed_error.xhtml
===================================================================
--- feeds100P26/view/error/feed_error.xhtml	                        (rev 0)
+++ feeds100P26/view/error/feed_error.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,15 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Feed not found
+    </ui:define>
+
+    <ui:define name="body">
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/error/post_error.xhtml
===================================================================
--- feeds100P26/view/error/post_error.xhtml	                        (rev 0)
+++ feeds100P26/view/error/post_error.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,15 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Post not found
+    </ui:define>
+
+    <ui:define name="body">
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/home.xhtml
===================================================================
--- feeds100P26/view/home.xhtml	                        (rev 0)
+++ feeds100P26/view/home.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,88 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:mamut="http://mamut.net.pl/jsf"
+                template="layout/template.xhtml">
+    <ui:define name="header">
+        JBoss.ORG Feeds home
+    </ui:define>
+
+    <ui:define name="body">
+        <div id="columnrightBLOG">
+            <div id="TwoColumnBlogJelly">
+                <s:link value="" view="/manage/remote/remote_propose.xhtml" propagation="none">
+                    <img src="/feeds/images/propose_blog.png"
+                         alt="Propose a Blog! If you are blogging on a JBoss-related subject, aggregate it in our system!" />
+                </s:link>
+            </div>
+
+            <div class="TwoColumnBlogSubnav">
+                <dt>Recent Posts</dt>
+                <!-- TODO: configure the number of posts -->
+                <ui:repeat var="post" value="#{feedsService.getPosts(0, 10)}">
+                    <dd>
+                        <s:link view="/view/post.xhtml" value="#{post.title}" propagation="none">
+                            <f:param name="post" value="#{post.titleAsId}"/>
+                        </s:link>
+                    </dd>
+                </ui:repeat>
+            </div>
+        </div>
+
+        <div id="columnleftBLOG">
+            <ui:repeat var="group" value="#{groupsService.allGroups}">
+                <mamut:let var="acceptedFeeds" value="#{groupsService.acceptedFeeds(group)}">
+                    <s:fragment rendered="#{acceptedFeeds.size() > 0}">
+                        <h4>#{group.displayName}</h4>
+                        <table cellspacing="5" class="laundrytable" width="75%">
+                            <tr>
+                                <th width="200px">Feed Author</th>
+                                <th width="480px">Feed Title</th>
+                            </tr>
+                            <ui:repeat var="feed" value="#{acceptedFeeds}">
+                                <tr>
+                                    <td>#{feed.author}</td>
+                                    <td>
+                                        <s:link view="/view/feed.xhtml" value="#{feed.title}" propagation="none">
+                                            <f:param name="name" value="#{feed.name}"/>
+                                        </s:link>
+                                    </td>
+                                </tr>
+                            </ui:repeat>
+                        </table>
+                    </s:fragment>
+                </mamut:let>
+
+                <s:fragment rendered="#{identity.loggedIn}">
+                    <mamut:let var="restrictedFeeds"
+                               value="#{feedsSecurity.filterViewableFeeds(groupsService.restrictedFeeds(group))}">
+                        <s:fragment rendered="#{restrictedFeeds.size() > 0}">
+                            <h4>#{group.displayName} (restricted)</h4>
+                            <table cellspacing="5" class="laundrytable" width="75%">
+                                <tr>
+                                    <th width="200px">Feed Author</th>
+                                    <th width="480px">Feed Title</th>
+                                </tr>
+                                <ui:repeat var="feed" value="#{restrictedFeeds}">
+                                    <tr>
+                                        <td>#{feed.author}</td>
+                                        <td>
+                                            <s:link view="/view/feed.xhtml" value="#{feed.title}" propagation="none">
+                                                <f:param name="name" value="#{feed.name}"/>
+                                            </s:link>
+                                        </td>
+                                    </tr>
+                                </ui:repeat>
+                            </table>
+                        </s:fragment>
+                    </mamut:let>
+                </s:fragment>
+            </ui:repeat>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/images/hdr_feed_gradient.gif
===================================================================
(Binary files differ)


Property changes on: feeds100P26/view/images/hdr_feed_gradient.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/view/images/ico_linkarrow_blue.gif
===================================================================
(Binary files differ)


Property changes on: feeds100P26/view/images/ico_linkarrow_blue.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/view/images/portlethdr_home.gif
===================================================================
(Binary files differ)


Property changes on: feeds100P26/view/images/portlethdr_home.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/view/images/propose_blog.png
===================================================================
(Binary files differ)


Property changes on: feeds100P26/view/images/propose_blog.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/view/images/propose_blog_full.png
===================================================================
(Binary files differ)


Property changes on: feeds100P26/view/images/propose_blog_full.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/view/images/wait.gif
===================================================================
(Binary files differ)


Property changes on: feeds100P26/view/images/wait.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: feeds100P26/view/index.html
===================================================================
--- feeds100P26/view/index.html	                        (rev 0)
+++ feeds100P26/view/index.html	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1 @@
+<html />
\ No newline at end of file

Added: feeds100P26/view/layout/menu.xhtml
===================================================================
--- feeds100P26/view/layout/menu.xhtml	                        (rev 0)
+++ feeds100P26/view/layout/menu.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,60 @@
+<div id="ORGheader"
+     xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:ui="http://java.sun.com/jsf/facelets"
+     xmlns:h="http://java.sun.com/jsf/html"
+     xmlns:f="http://java.sun.com/jsf/core"
+     xmlns:s="http://jboss.com/products/seam/taglib"
+     xmlns:rich="http://richfaces.org/rich">
+    <div id="ORGLogo">
+        <a href="/">
+            <img src="http://labs.jboss.com/file-access/default/theme/images/common/jbossorg_logo.gif"
+                 alt="JBoss.org home" width="250" height="65" border="0"/>
+        </a>
+    </div>
+
+    <div id='utilitynav'>
+        <h:form styleClass="nomargin" id="TopSearch">
+            <ul>
+                <li>
+                    <s:fragment rendered="#{!identity.loggedIn}">
+                        <s:link view="/security/login.xhtml" value="Login" />&#160;&#160;|&#160;&#160;
+                    </s:fragment>
+                    <s:fragment rendered="#{identity.loggedIn}">
+                        #{identity.username} &#160;&#160;|&#160;&#160;
+                        <s:link action="#{identity.logout}" value="Logout" />&#160;&#160;|&#160;&#160;
+                    </s:fragment>
+                </li>
+                <li><a href="https://www.redhat.com/apps/store/jboss/">Subscribe</a>&#160;&#160;|&#160;&#160;</li>
+                <li><a href="http://www.jboss.com/index.html?op=checkage&amp;module=user">Register</a>&#160;&#160;|&#160;&#160;</li>
+                <li>
+                    Search:
+                    <h:inputText style="border: 1px solid #6e6e6e; font: 10px Verdana, Helvetica, Arial, sans-serif; padding-left: 2px; margin-left: 10px;"
+                                 value="#{postSearch.query}" />
+                    <h:commandButton value="Search" action="/search/search.xhtml"
+                                     image="http://labs.jboss.com/file-access/default/theme/images/common/ico_searcharrow.gif"
+                                     style="width:13px; height:13px">
+                        <s:conversationPropagation value="none" />
+                    </h:commandButton>
+                </li>
+            </ul>
+        </h:form>
+    </div>
+
+    <!-- Primary Navigation -->
+    <div id="primarynav">
+        <ul>
+            <li>
+                <a href="/">Home</a>
+            </li>
+            <li>
+                <a href="/resources">Resources</a>
+            </li>
+            <li>
+                <a href="/projects">Projects</a>
+            </li>
+            <li id='current'>
+                <a href="/community">Community</a>
+            </li>
+        </ul>
+    </div>
+</div>

Added: feeds100P26/view/layout/template.xhtml
===================================================================
--- feeds100P26/view/layout/template.xhtml	                        (rev 0)
+++ feeds100P26/view/layout/template.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,78 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:ui="http://java.sun.com/jsf/facelets"
+      xmlns:h="http://java.sun.com/jsf/html"
+      xmlns:f="http://java.sun.com/jsf/core"
+      xmlns:s="http://jboss.com/products/seam/taglib"
+      xmlns:mamut="http://mamut.net.pl/jsf"
+      xmlns:a="http://richfaces.org/a4j">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>JBoss.ORG Feeds</title>
+    <link href="/feeds/stylesheet/org_main.css" rel="stylesheet" type="text/css" />
+    <link href="/feeds/stylesheet/org_layout.css" rel="stylesheet" type="text/css" />
+    <link href="/feeds/stylesheet/blog.css" rel="stylesheet" type="text/css" />
+    <ui:insert name="additional_headers" />
+</head>
+
+<body>
+<div id="container">
+    <ui:include src="menu.xhtml" />
+
+    <div id="contentcontainer">
+        <div id="ORGContent">
+            <div id='orgprojectdetailstriple'>
+                <mamut:let var="canViewManagement" value="#{identity.hasPermission('management', 'view')}">
+                    <s:fragment rendered="#{canViewManagement}">
+                        <div class="adminlinks">
+                            <s:fragment rendered="#{identity.hasPermission('useraccount', 'view')}">
+                                <s:link view="/security/account.xhtml" value="User account" propagation="none" />&#160;&#160;&#160;
+                            </s:fragment>
+                            <s:link view="/home.xhtml" value="Feeds home" propagation="none" />&#160;&#160;&#160;
+                            <s:link view="/manage/index.xhtml" value="Manage home" propagation="none" />
+                        </div>
+                    </s:fragment>
+                    <s:fragment rendered="#{identity.hasPermission('useraccount', 'view') and !canViewManagement}">
+                        <div class="adminlinks">
+                            <s:link view="/security/account.xhtml" value="User account" propagation="none" />
+                        </div>
+                    </s:fragment>
+                </mamut:let>
+
+                <h3 id="majorsectiontitle" style="margin-bottom: 0"><ui:insert name="header"/></h3>
+            </div>
+            <div id="orghomemaximized">
+                <div>
+                    <h:messages globalOnly="true"
+                                infoClass="messages_info"
+                                warnClass="messages_warn"
+                                errorClass="messages_warn"
+                                fatalClass="messages_warn"
+                                styleClass="messages"
+                            />
+                </div>
+
+                <ui:insert name="body"/>
+            </div>
+        </div>
+
+        <div class="footer">
+            <p>
+                <a href="http://www.redhat.com/">Red Hat.com</a>&nbsp;&nbsp;|&nbsp;&nbsp;
+                <a href="http://www.jboss.com/">JBoss.com</a>&nbsp;&nbsp;|&nbsp;&nbsp;
+                <a href="http://www.jboss.com/company/contact">Contact Us</a>&nbsp;&nbsp;|&nbsp;&nbsp;
+                <a href="http://www.redhat.com/about/careers">Careers</a>
+            </p>
+            <p>
+                Hosted on <a href="http://labs.jboss.com/jbosslabs">JBoss Labs</a>.
+                Powered by <a href="http://labs.jboss.com/jbossas">JBoss AS</a>,
+                <a href="http://labs.jboss.com/jbossportal">JBoss Portal</a>,
+                <a href="http://labs.jboss.com/drools">Drools</a>and
+                <a href="http://labs.jboss.com/jbossseam">JBoss Seam</a>.
+            </p>
+        </div>
+    </div>
+</div>
+</body>
+</html>

Added: feeds100P26/view/manage/aggregated/aggregated_add.xhtml
===================================================================
--- feeds100P26/view/manage/aggregated/aggregated_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/aggregated/aggregated_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Add aggregated feed
+    </ui:define>
+    
+    <ui:define name="body">
+        <ui:include src="aggregated_mod.xhtml">
+            <ui:param name="new" value="true" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/aggregated/aggregated_edit.xhtml
===================================================================
--- feeds100P26/view/manage/aggregated/aggregated_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/aggregated/aggregated_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,19 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit aggregated feed: #{feedMod.feed.name}
+    </ui:define>
+
+    <ui:define name="body"><ui:include src="aggregated_mod.xhtml">
+            <ui:param name="new" value="false" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/aggregated/aggregated_mod.xhtml
===================================================================
--- feeds100P26/view/manage/aggregated/aggregated_mod.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/aggregated/aggregated_mod.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,140 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+<div class="TwoColumnBlogSubnav">
+    <h4>Tips</h4>
+    <ul>
+        <li>
+            If you choose to aggregate a feed group, all feeds from that group will be aggregated.
+        </li>
+        <li class="last">
+            Filters will be applied to each post in a feed, group, or to all posts (depending where you choose to
+                place the filter), and if the filter output is positive the post will be included in the aggregated
+                feed.
+        </li>
+    </ul>
+</div>
+
+<h:form>
+<div class="adminforms">
+<h4>Feeds to include:</h4>
+
+<h:panelGroup>
+    <h:dataTable value="#{aggregatedFeedMod.availableFeeds}" var="feed" id="availableFeedsTable"
+                 styleClass="basetablestyle" headerClass="header tableheader" rowClasses="oddRow,evenRow"
+                 cellspacing="0" cellpadding="4">
+        <h:column>
+            <h:selectBooleanCheckbox value="#{aggregatedFeedMod.selectedFeeds[feed]}" styleClass="radios">
+                <a:support event="onclick" reRender="availableFeedsTable" />
+            </h:selectBooleanCheckbox>
+        </h:column>
+        <h:column>
+            <f:facet name="header">
+                Feed name:
+            </f:facet>
+            #{feed.name}
+        </h:column>
+        <h:column>
+            <f:facet name="header">
+                Local filters, applied only to this feed:
+            </f:facet>
+            <h:panelGroup rendered="#{aggregatedFeedMod.selectedFeeds[feed]}">
+                <ul>
+                    <ui:repeat var="filter" value="#{aggregatedFeedMod.selectedFeedsFilters[feed]}">
+                        <li>
+                            #{filter}
+                            (<h:commandLink action="#{aggregatedFeedMod.removeFilter(aggregatedFeedMod.selectedFeedsFilters[feed],filter)}"
+                                            value="Remove" />)
+                        </li>
+                    </ui:repeat>
+                </ul>
+
+                <h:commandLink action="#{filterAdd.addToList(aggregatedFeedMod.selectedFeedsFilters[feed])}"
+                               value="Add filter" />
+            </h:panelGroup>
+        </h:column>
+    </h:dataTable>
+</h:panelGroup>
+
+<h4>Feed groups to include:</h4>
+
+<h:panelGroup>
+    <h:dataTable value="#{aggregatedFeedMod.availableGroups}" var="group" id="availableGroupsTable"
+                 styleClass="basetablestyle" headerClass="header tableheader" rowClasses="oddRow,evenRow"
+                 cellspacing="0" cellpadding="4">
+        <h:column>
+            <h:selectBooleanCheckbox value="#{aggregatedFeedMod.selectedGroups[group]}" styleClass="radios">
+                <a:support event="onclick" reRender="availableGroupsTable" />
+            </h:selectBooleanCheckbox>
+        </h:column>
+        <h:column>
+            <f:facet name="header">
+                Group name:
+            </f:facet>
+            #{group.displayName}
+        </h:column>
+        <h:column>
+            <f:facet name="header">
+                Local filters, applied only to this group:
+            </f:facet>
+            <h:panelGroup rendered="#{aggregatedFeedMod.selectedGroups[group]}">
+                <ul>
+                    <ui:repeat var="filter" value="#{aggregatedFeedMod.selectedGroupsFilters[group]}">
+                        <li>
+                            #{filter}
+                            (<h:commandLink action="#{aggregatedFeedMod.removeFilter(aggregatedFeedMod.selectedGroupsFilters[group],filter)}"
+                                            value="Remove" />)
+                        </li>
+                    </ui:repeat>
+                </ul>
+
+                <h:commandLink action="#{filterAdd.addToList(aggregatedFeedMod.selectedGroupsFilters[group])}"
+                               value="Add filter" />
+            </h:panelGroup>
+        </h:column>
+    </h:dataTable>
+</h:panelGroup>
+
+<h4>Global filters, applied to all feeds:</h4>
+
+<ul>
+    <ui:repeat var="filter" value="#{aggregatedFeedMod.globalFilters}">
+        <li>
+            #{filter}
+            (<h:commandLink action="#{aggregatedFeedMod.removeFilter(aggregatedFeedMod.globalFilters,filter)}"
+                            value="Remove" />)
+        </li>
+    </ui:repeat>
+</ul>
+
+<h:commandLink action="#{filterAdd.addToList(aggregatedFeedMod.globalFilters)}" value="Add filter" />
+
+<div id="proceed" class="formbuttons">
+    <ul>
+        <s:fragment rendered="#{new}">
+            <li>
+                <h:commandButton value="Next &#187;" action="#{aggregatedFeedMod.saveNew}" styleClass="submit" />
+            </li>
+        </s:fragment>
+        <s:fragment rendered="#{!new}">
+            <li>
+                <h:commandButton value="Save" action="#{aggregatedFeedMod.saveExisting}" styleClass="submit" />
+            </li>
+        </s:fragment>
+        <li>
+            <s:button value="Cancel" view="/manage/index.xhtml" propagation="end" styleClass="submit" />
+        </li>
+        <li>
+            <ui:include src="../../common/ajax_status.xhtml" />
+        </li>
+    </ul>
+</div>
+</div>
+</h:form>
+</ui:composition>

Added: feeds100P26/view/manage/aggregated/filter_add.xhtml
===================================================================
--- feeds100P26/view/manage/aggregated/filter_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/aggregated/filter_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,111 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+<ui:define name="header">
+    Add a filter feed
+</ui:define>
+
+<ui:define name="body">
+    <div class="TwoColumnBlogSubnav">
+        <h4>Tips</h4>
+        <ul>
+            <li>
+                An <a href="http://www.regular-expressions.info/javascriptexample.html" target="_blank">online regular
+                expressions tester</a> in case you need help.
+            </li>
+        </ul>
+    </div>
+
+    <div class="adminforms">
+        <table class="basetablestyle" cellspacing="0" cellpadding="4">
+            <tr>
+                <th class="header tableHeader">Filter function</th>
+                <th class="header tableHeader">Configure/add</th>
+            </tr>
+            <tr class="oddRow">
+                <td>Only entries with enclosures filter (podcast):</td>
+                <td>
+                    <h:form>
+                        <h:commandButton value="Add" action="#{filterAdd.add(filterAdd.podcastFilter)}"
+                                         styleClass="submit" />
+                    </h:form>
+                </td>
+            </tr>
+            <tr class="evenRow">
+                <td>Only entries without enclosure filter (not-podcast):</td>
+                <td>
+                    <h:form>
+                        <h:commandButton value="Add" action="#{filterAdd.add(filterAdd.notPodcastFilter)}"
+                                         styleClass="submit" />
+                    </h:form>
+                </td>
+            </tr>
+            <tr class="oddRow">
+                <td>Only entries whose author matches a regular expression:</td>
+                <td>
+                    <h:form>
+                        <h:outputLabel for="author">
+                            <span class="required">*</span> Regular expression:
+                        </h:outputLabel>
+                        <br />
+                        <h:panelGroup>
+                            <h:inputText id="author" value="#{filterAdd.authorRegexpFilter.regexp}" required="true" size="16">
+                                <a:support event="onblur" reRender="authorMessage" ajaxSingle="true" bypassUpdates="true"/>
+                                <s:validate />
+                            </h:inputText>
+
+                            <a:outputPanel id="authorMessage">
+                                <h:message for="author" styleClass="error" />
+                            </a:outputPanel>
+                        </h:panelGroup>
+                        <br />
+                        <h:commandButton value="Add" action="#{filterAdd.add(filterAdd.authorRegexpFilter)}"
+                                         styleClass="submit" />
+                    </h:form>
+                </td>
+            </tr>
+            <tr class="evenRow">
+                <td>Only entries where at least one category matches a regular expression:</td>
+                <td>
+                    <h:form>
+                        <h:outputLabel for="category">
+                            <span class="required">*</span> Regular expression:
+                        </h:outputLabel>
+                        <br />
+                        <h:panelGroup>
+                            <h:inputText id="category" value="#{filterAdd.categoryRegexpFilter.regexp}" required="true" size="16">
+                                <a:support event="onblur" reRender="categoryMessage" ajaxSingle="true" bypassUpdates="true"/>
+                                <s:validate />
+                            </h:inputText>
+
+                            <a:outputPanel id="categoryMessage">
+                                <h:message for="category" styleClass="error" />
+                            </a:outputPanel>
+                        </h:panelGroup>
+                        <br />
+                        <h:commandButton value="Add" action="#{filterAdd.add(filterAdd.categoryRegexpFilter)}"
+                                         styleClass="submit" />
+                    </h:form>
+                </td>
+            </tr>
+        </table>
+
+        <h:form>
+            <div id="proceed" class="formbuttons">
+                <ul>
+                    <li>
+                        <h:commandButton value="Cancel" action="#{filterAdd.cancel}" styleClass="submit"/>
+                    </li>
+                </ul>
+            </div>
+        </h:form>
+    </div>
+</ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/configuration_manager.xhtml
===================================================================
--- feeds100P26/view/manage/configuration_manager.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/configuration_manager.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,42 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Configuration manager
+    </ui:define>
+    <ui:define name="body">
+        <h:form>
+            <div class="adminforms">
+                <h:panelGrid columns="2">
+                    <h:outputLabel for="adminEmail">Administrator email:</h:outputLabel>
+                    <h:panelGroup>
+                        <h:inputText id="adminEmail" value="#{configurationManager.configuration.adminEmail}" size="16">
+                            <a:support event="onblur" reRender="adminEmailMessage" ajaxSingle="true" bypassUpdates="true"/>
+                            <s:validate />
+                        </h:inputText>
+                        <a:outputPanel id="adminEmailMessage">
+                            <h:message for="adminEmail" styleClass="error" />
+                        </a:outputPanel>
+                    </h:panelGroup>
+                </h:panelGrid>
+                <div class="formbuttons">
+                    <ul>
+                        <li>
+                            <h:commandButton value="Save changes" action="#{configurationManager.save}" styleClass="submit" />
+                        </li>
+                        <li>
+                            <h:commandButton value="Save and test email" action="#{configurationManager.testEmail}" styleClass="submit" />
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </h:form>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/feed_add.xhtml
===================================================================
--- feeds100P26/view/manage/feed_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/feed_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Add a new feed - edit data
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="feed_mod.xhtml">
+            <ui:param name="new" value="true" />
+            <ui:param name="advanced" value="true" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/feed_edit.xhtml
===================================================================
--- feeds100P26/view/manage/feed_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/feed_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Edit feed: #{feedMod.feed.name}
+    </ui:define>
+    
+    <ui:define name="body">
+        <ui:include src="feed_mod.xhtml">
+            <ui:param name="new" value="false" />
+            <ui:param name="advanced" value="true" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/feed_mod.xhtml
===================================================================
--- feeds100P26/view/manage/feed_mod.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/feed_mod.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,199 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:blog="http://jboss.org/blog/tags"
+                xmlns:a="http://richfaces.org/a4j">
+<div class="TwoColumnBlogSubnav">
+    <h4>Tips</h4>
+    <ul>
+        <li class="last">
+            Please fill in all the details that are necessary to handle your feed.
+        </li>
+    </ul>
+</div>
+
+<h:form>
+<div class="adminforms">
+<h:panelGrid columns="2">
+<h:outputLabel for="name"><span class="required">*</span> Name (to use in URLs):</h:outputLabel>
+<h:panelGroup>
+    <h:inputText id="name" value="#{feedMod.feed.name}" required="true" size="16">
+        <blog:uniqueFeedNameValidator entityId="#{feedMod.feed.id}" />
+        <a:support event="onblur" reRender="nameMessage" ajaxSingle="true" bypassUpdates="true"/>
+        <s:validate />
+    </h:inputText>
+    <a:outputPanel id="nameMessage">
+        <h:message for="name" styleClass="error" />
+    </a:outputPanel>
+</h:panelGroup>
+
+<h:outputLabel for="title"><span class="required">*</span> Title:</h:outputLabel>
+<h:panelGroup>
+    <h:inputText id="title" value="#{feedMod.feed.title}" required="true" size="32">
+        <a:support event="onblur" reRender="titleMessage" ajaxSingle="true" bypassUpdates="true"/>
+        <s:validate />
+    </h:inputText>
+
+    <a:outputPanel id="titleMessage">
+        <h:message for="title" styleClass="error" />
+    </a:outputPanel>
+</h:panelGroup>
+
+<h:outputLabel for="author"><span class="required">*</span> Author:</h:outputLabel>
+<h:panelGroup>
+    <h:inputText id="author" value="#{feedMod.feed.author}" required="true" size="32">
+        <a:support event="onblur" reRender="authorMessage" ajaxSingle="true" bypassUpdates="true"/>
+        <s:validate />
+    </h:inputText>
+
+    <a:outputPanel id="authorMessage">
+        <h:message for="author" styleClass="error" />
+    </a:outputPanel>
+</h:panelGroup>
+
+<!--<h:outputLabel for="description">Description:</h:outputLabel>
+<h:panelGroup>
+    <h:inputTextarea id="description" value="#{feedMod.feed.description}" rows="4" cols="32">
+        <a:support event="onblur" reRender="descriptionMessage" ajaxSingle="true" bypassUpdates="true"/>
+        <s:validate />
+    </h:inputTextarea>
+
+    <a:outputPanel id="descriptionMessage">
+        <h:message for="description" styleClass="error" />
+    </a:outputPanel>
+</h:panelGroup>-->
+
+<h:outputLabel for="link" rendered="#{not (feedMod.feed.link == null)}">Link to blog:</h:outputLabel>
+<h:panelGroup rendered="#{not (feedMod.feed.link == null)}">
+    <h:inputText id="link" value="#{feedMod.feed.link}" size="48" maxlength="64" disabled="true">
+        <s:validate />
+    </h:inputText>
+</h:panelGroup>
+
+<h:outputLabel for="group"><span class="required">*</span> Project/ group:</h:outputLabel>
+<h:panelGroup>
+    <h:selectOneMenu id="group" required="true" value="#{feedMod.feed.group}">
+        <a:support event="onblur" reRender="groupMessage" ajaxSingle="true" bypassUpdates="true"/>
+        <s:convertEntity />
+        <s:selectItems var="group" value="#{groupsSecurity.filterForFeedMod(feedMod.feed, groupsService.allGroups, new)}"
+                       label="#{group.displayName}" />
+    </h:selectOneMenu>
+
+    <a:outputPanel id="groupMessage">
+        <h:message for="group" styleClass="error" />
+    </a:outputPanel>
+
+    <s:div rendered="#{identity.hasPermission('group', 'add')}">
+        <s:link value="Add new group" action="#{groupMod.add}" /><br />
+    </s:div>
+</h:panelGroup>
+
+<h:outputLabel for="maxPostsInFeed">
+    <span class="required">*</span> Maximum number of posts in an atom feed:
+</h:outputLabel>
+<h:panelGroup>
+    <h:inputText id="maxPostsInFeed" value="#{feedMod.feed.maxPostsInFeed}" required="true" size="16">
+        <a:support event="onblur" reRender="maxPostsInFeedMessage" ajaxSingle="true" bypassUpdates="true"/>
+        <s:validate />
+    </h:inputText>
+
+    <a:outputPanel id="maxPostsInFeedMessage">
+        <h:message for="maxPostsInFeed" styleClass="error" />
+    </a:outputPanel>
+</h:panelGroup>
+
+<h:outputLabel for="maxPostsOnPage">
+    <span class="required">*</span> Maximum number of posts to display on one page:
+</h:outputLabel>
+<h:panelGroup>
+    <h:inputText id="maxPostsOnPage" value="#{feedMod.feed.maxPostsOnPage}" required="true" size="16">
+        <a:support event="onblur" reRender="maxPostsOnPageMessage" ajaxSingle="true" bypassUpdates="true"/>
+        <s:validate />
+    </h:inputText>
+
+    <a:outputPanel id="maxPostsOnPageMessage">
+        <h:message for="maxPostsOnPage" styleClass="error" />
+    </a:outputPanel>
+</h:panelGroup>
+
+<h:outputLabel for="showDelicious">
+    Add a "Post to del.icio.us" link to every post:
+</h:outputLabel>
+<h:panelGroup>
+    <h:selectBooleanCheckbox id="showDelicious" value="#{feedMod.feed.showDelicious}" />
+</h:panelGroup>
+
+<h:outputLabel for="showDigg">
+    Add a "Digg this!" link to every post:
+</h:outputLabel>
+<h:panelGroup>
+    <h:selectBooleanCheckbox id="showDigg" value="#{feedMod.feed.showDigg}" />
+</h:panelGroup>
+
+<h:outputLabel for="showDzone">
+    Add a "Post to DZone" link to every post:
+</h:outputLabel>
+<h:panelGroup>
+    <h:selectBooleanCheckbox id="showDzone" value="#{feedMod.feed.showDzone}" />
+</h:panelGroup>
+
+<h:outputLabel for="showStumble">
+    Add a "Stumble It!" link to every post:
+</h:outputLabel>
+<h:panelGroup>
+    <h:selectBooleanCheckbox id="showStumble" value="#{feedMod.feed.showStumble}" />
+</h:panelGroup>
+
+<h:outputLabel for="restricted" rendered="#{advanced}">
+    This is a restricted feed, viewable only by authorized users:
+</h:outputLabel>
+<h:panelGroup rendered="#{advanced}">
+    <h:selectBooleanCheckbox id="restricted" value="#{feedMod.feed.restricted}" />
+</h:panelGroup>
+
+<h:outputLabel><span class="required">*</span> Xml templates:</h:outputLabel>
+<h:panelGroup>
+    <h:dataTable var="templateType" value="#{feedMod.templateTypes}">
+        <h:column>
+            #{templateType}:
+        </h:column>
+        <h:column>
+            <h:selectOneMenu value="#{feedMod.feed.templates[templateType]}">
+                <s:convertEntity />
+                <s:selectItems var="template" value="#{templateService.templatesOfType(templateType)}"
+                               label="#{template.name}" />
+            </h:selectOneMenu>
+        </h:column>
+    </h:dataTable>
+</h:panelGroup>
+
+</h:panelGrid>
+
+<div class="formbuttons">
+    <ul>
+        <s:fragment rendered="#{new}">
+            <li>
+                <h:commandButton value="Add" action="#{feedMod.saveNew}" rendered="#{new}" styleClass="submit" />
+            </li>
+        </s:fragment>
+        <s:fragment rendered="#{!new}">
+            <li>
+                <h:commandButton value="Save" action="#{feedMod.saveExisting}" rendered="#{!new}" styleClass="submit" />
+            </li>
+        </s:fragment>
+        <li>
+            <s:button value="Cancel" view="/manage/index.xhtml" propagation="end" styleClass="submit"
+                      rendered="#{identity.hasPermission('management', 'view')}"/>
+            <s:button value="Cancel" view="/home.xhtml" propagation="end" styleClass="submit"
+                      rendered="#{!identity.hasPermission('management', 'view')}"/>
+        </li>
+    </ul>
+</div>
+</div>
+</h:form>
+</ui:composition>

Added: feeds100P26/view/manage/feed_propose.xhtml
===================================================================
--- feeds100P26/view/manage/feed_propose.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/feed_propose.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Propose a new feed - edit data
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="feed_mod.xhtml">
+            <ui:param name="new" value="true" />
+            <ui:param name="advanced" value="false" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/group/group_add.xhtml
===================================================================
--- feeds100P26/view/manage/group/group_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/group/group_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Add feed group
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="group_mod.xhtml">
+            <ui:param name="new" value="true" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/group/group_edit.xhtml
===================================================================
--- feeds100P26/view/manage/group/group_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/group/group_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit feed group: #{groupMod.group.displayName}
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="group_mod.xhtml">
+            <ui:param name="new" value="false" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/group/group_list.xhtml
===================================================================
--- feeds100P26/view/manage/group/group_list.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/group/group_list.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,56 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Manage groups
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="adminlist">
+            <dl>
+                <dd><s:link value="Add new group" action="#{groupMod.add}" /></dd>
+                <dt>Current groups:</dt>
+                <hr />
+            </dl>
+        </div>
+
+        <table border="0" cellpadding="4" cellspacing="0" class="basetablestyle" style="margin-top:12px;">
+            <tr class="header">
+                <td class="tableheaderfirst" style="width:160px;">Group display name</td>
+                <td class="tableheader">Group name</td>
+                <td class="tableheader" />
+                <td class="tableheader" />
+            </tr>
+
+            <a:repeat var="group" value="#{groupsService.allGroups}" rowKeyVar="rowNumber">
+                <s:fragment rendered="#{identity.hasPermission('group', 'edit', group) ||
+                    identity.hasPermission('group', 'delete', group)}">
+                    <tr class="#{(rowNumber%2 == 0) ? 'evenRow' : 'oddRow'}">
+                        <td class="rowlinefirst" style="font-weight:bold;">#{group.displayName}</td>
+                        <td class="rowline">#{group.name}</td>
+                        <td class="rowline">
+                            <s:link action="#{groupMod.edit}" value="Edit"
+                                    rendered="#{identity.hasPermission('group', 'edit', group)}">
+                                <f:param name="id" value="#{group.id}" />
+                            </s:link>
+                        </td>
+                        <td class="rowline">
+                            <s:link view="/manage/group/group_delete.xhtml" action="#{groupMod.delete}" value="Delete"
+                                    onclick="if (!confirm('Are you sure you want to delete this group?')) return false"
+                                    rendered="#{identity.hasPermission('group', 'delete', group)}">
+                                <f:param name="id" value="#{group.id}" />
+                            </s:link>
+                        </td>
+                    </tr>
+                </s:fragment>
+            </a:repeat>
+        </table>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/group/group_mod.xhtml
===================================================================
--- feeds100P26/view/manage/group/group_mod.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/group/group_mod.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,110 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:blog="http://jboss.org/blog/tags"
+                xmlns:a="http://richfaces.org/a4j">
+    <div class="TwoColumnBlogSubnav">
+        <h4>Tips</h4>
+        <ul>
+            <li>
+                Notifications of new proposed feeds in this group will be sent to the e-mail
+                specified below (field "Administrator e-mail").
+            </li>
+            <li class="last">
+                Header is an optional piece of HTML that will be displayed on the top of the page when
+                    viewing feeds/posts from that group.
+            </li>
+        </ul>
+    </div>
+
+    <div class="adminforms">
+        <h:form>
+            <h:panelGrid columns="2">
+                <h:outputLabel for="name"><span class="required">*</span> Name (to use in URLs):</h:outputLabel>
+                <h:panelGroup>
+                    <h:inputText id="name" value="#{groupMod.group.name}" required="true" size="32">
+                        <blog:uniqueGroupNameValidator entityId="#{groupMod.group.id}" />
+                        <a:support event="onblur" reRender="nameMessage" ajaxSingle="true" bypassUpdates="true"/>
+                        <s:validate />
+                    </h:inputText>
+
+                    <a:outputPanel id="nameMessage">
+                        <h:message for="name" styleClass="error" />
+                    </a:outputPanel>
+                </h:panelGroup>
+
+                <h:outputLabel for="displayName"><span class="required">*</span> Display name:</h:outputLabel>
+                <h:panelGroup>
+                    <h:inputText id="displayName" value="#{groupMod.group.displayName}" required="true" size="32">
+                        <a:support event="onblur" reRender="displayNameMessage" ajaxSingle="true" bypassUpdates="true"/>
+                        <s:validate />
+                    </h:inputText>
+
+                    <a:outputPanel id="displayNameMessage">
+                        <h:message for="displayName" styleClass="error" />
+                    </a:outputPanel>
+                </h:panelGroup>
+
+                <h:outputLabel for="adminEmail">Administrator e-mail:</h:outputLabel>
+                <h:panelGroup>
+                    <h:inputText id="adminEmail" value="#{groupMod.group.adminEmail}" size="32">
+                        <a:support event="onblur" reRender="adminEmailMessage" ajaxSingle="true" bypassUpdates="true"/>
+                        <s:validate />
+                    </h:inputText>
+
+                    <a:outputPanel id="adminEmailMessage">
+                        <h:message for="adminEmail" styleClass="error" />
+                    </a:outputPanel>
+                </h:panelGroup>
+
+                <h:outputLabel for="header">Header:</h:outputLabel>
+                <h:panelGroup>
+                    <h:inputTextarea id="header" value="#{groupMod.group.header}" rows="25" cols="60">
+                        <a:support event="onblur" reRender="headerMessage" ajaxSingle="true" bypassUpdates="true"/>
+                        <s:validate />
+                    </h:inputTextarea>
+
+                    <a:outputPanel id="headerMessage">
+                        <h:message for="header" styleClass="error" />
+                    </a:outputPanel>
+                </h:panelGroup>
+            </h:panelGrid>
+
+            <div id="proceed" class="formbuttons">
+                <ul>
+                    <s:fragment rendered="#{new}">
+                        <li>
+                            <h:commandButton value="Add" action="#{groupMod.saveNew}" styleClass="submit" />
+                        </li>
+                    </s:fragment>
+                    <s:fragment rendered="#{!new}">
+                        <li>
+                            <h:commandButton value="Save" action="#{groupMod.saveExisting}" styleClass="submit" />
+                        </li>
+                    </s:fragment>
+                    <li>
+                        <a:commandButton value="Update preview" reRender="preview,displayNameMessage,nameMessage"
+                                         styleClass="submit" />
+                    </li>
+                    <li>
+                        <s:button value="Cancel" action="#{groupMod.cancel}" styleClass="submit" />
+                    </li>
+                    <li>
+                        <ui:include src="../../common/ajax_status.xhtml" />
+                    </li>
+                </ul>
+            </div>
+
+            <s:div styleClass="formbuttons" id="preview">
+                Preview of the header: <br />
+
+                <h:outputText value="#{groupMod.group.header}" escape="false" />
+            </s:div>
+        </h:form>
+    </div>
+</ui:composition>

Added: feeds100P26/view/manage/highlights/highlights_edit.xhtml
===================================================================
--- feeds100P26/view/manage/highlights/highlights_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/highlights/highlights_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,18 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit highlights feed: #{feedMod.feed.name}
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="highlights_mod.xhtml" />
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/highlights/highlights_mod.xhtml
===================================================================
--- feeds100P26/view/manage/highlights/highlights_mod.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/highlights/highlights_mod.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,71 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+    <div class="adminforms">
+        <h:form>
+            <s:div rendered="#{highlightsFeedMod.highlightsFeed.selectedPosts.size == 0}">
+                There are no posts selected.
+            </s:div>
+
+            <s:div id="postsTable">
+                <table class="basetablestyle" >
+                    <a:repeat rowKeyVar="rowNumber" var="post" value="#{highlightsFeedMod.highlightsFeed.selectedPosts}">
+                        <tr class="#{(rowNumber%2 == 0) ? 'evenRow' : 'oddRow'}">
+                            <td>
+                                #{rowNumber}
+                            </td>
+                            <td>
+                                <ui:include src="../../common/post.xhtml">
+                                    <ui:param name="post" value="#{post}" />
+                                    <ui:param name="showSummary" value="true" />
+                                    <ui:param name="showAddToHighlights" value="false" />
+                                </ui:include>
+                            </td>
+                            <td>
+                                <a:commandLink value="Move up" action="#{highlightsFeedMod.moveUp(rowNumber)}"
+                                               reRender="postsTable" rendered="#{rowNumber != 0}" />
+                            </td>
+                            <td>
+                                <a:commandLink value="Move down" action="#{highlightsFeedMod.moveDown(rowNumber)}"
+                                               reRender="postsTable"
+                                               rendered="#{rowNumber != highlightsFeedMod.highlightsFeed.selectedPosts.size()-1}"/>
+                            </td>
+                            <td>
+                                <a:commandLink value="Move to position:" action="#{highlightsFeedMod.moveTo(rowNumber)}"
+                                               reRender="postsTable" />
+                            </td>
+                            <td>
+                                <h:inputText size="2" maxlength="2" value="#{highlightsFeedMod.positions[rowNumber]}" />
+                            </td>
+                            <td>
+                                <a:commandLink value="Delete" action="#{highlightsFeedMod.delete(rowNumber)}"
+                                               reRender="postsTable" />
+                            </td>
+                        </tr>
+                    </a:repeat>
+                </table>
+            </s:div>
+
+            <s:div styleClass="formbuttons">
+                <ul>
+                    <li>
+                        <h:commandButton value="Save" action="#{highlightsFeedMod.saveExisting}"
+                                         styleClass="submit" />
+                    </li>
+                    <li>
+                        <s:button value="Cancel" view="/manage/index.xhtml" propagation="end" styleClass="submit" />
+                    </li>
+                    <li>
+                        <ui:include src="../../common/ajax_status.xhtml" />
+                    </li>
+                </ul>
+            </s:div>
+        </h:form>
+    </div>
+</ui:composition>

Added: feeds100P26/view/manage/highlights/post_add.xhtml
===================================================================
--- feeds100P26/view/manage/highlights/post_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/highlights/post_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,53 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Add post '#{highlightsFeedMod.post.title}' to a highlights feed
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="TwoColumnBlogSubnav">
+            <h4>Tips</h4>
+            <ul>
+                <li id="last">
+                    After selecting a highlights feed, you'll be able to change the order of the posts.
+                </li>
+            </ul>
+        </div>
+
+        <div class="adminforms">
+            <h:form>
+                <h:panelGrid columns="2">
+                    <h:outputLabel for="feeds">Highlights feed:</h:outputLabel>
+                    <h:panelGroup>
+                        <h:selectOneListbox id="feeds" value="#{highlightsFeedMod.selectedFeed}" required="true">
+                            <s:selectItems var="feed" value="#{highlightsSecurity.feeds}" label="#{feed.title}" />
+                            <s:convertEntity />
+                        </h:selectOneListbox>
+
+                        <a:outputPanel id="feedsMessage">
+                            <h:message for="feeds" styleClass="error" />
+                        </a:outputPanel>
+                    </h:panelGroup>
+                </h:panelGrid>
+                <div id="proceed" class="formbuttons">
+                    <ul>
+                        <li>
+                            <h:commandButton value="Select" action="#{highlightsFeedMod.addPost}" styleClass="submit"/>
+                        </li>
+                        <li>
+                            <s:button value="Cancel" view="/home.xhtml" styleClass="submit"/>
+                        </li>
+                    </ul>
+                </div>
+            </h:form>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/index.xhtml
===================================================================
--- feeds100P26/view/manage/index.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/index.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,172 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                xmlns:mamut="http://mamut.net.pl/jsf"
+                template="../layout/template.xhtml">
+<ui:define name="header">
+    Manage feeds
+</ui:define>
+<ui:define name="body">
+<div class="TwoColumnBlogSubnav">
+    <h4>Tips</h4>
+    <ul>
+        <li class="last">
+            The <a href="http://labs.jboss.com/files/jbosslabs/feeds_tutorial/tutorial.html">tutorial</a> may be
+            helpful.
+        </li>
+    </ul>
+</div>
+
+<div class="adminlist">
+    <dl>
+        <s:fragment rendered="#{propositionsTools.pendingPropositions > 0 ||
+                    identity.hasPermission('feed', 'add_any')}">
+            <dt>New feed operations:</dt>
+            <hr />
+            <s:fragment rendered="#{propositionsTools.pendingPropositions > 0}">
+                <dd>
+                    <s:link value="Pending feed propositions (#{propositionsTools.pendingPropositions})"
+                            view="/manage/proposition/proposition_list.xhtml" />
+                </dd>
+            </s:fragment>
+            <s:fragment rendered="#{identity.hasPermission('feed', 'add_any')}">
+                <ui:repeat var="feedType" value="#{feedTypes.allTypes}">
+                    <dd>
+                        <s:link value="Add a new #{feedType.name()} feed" view="#{feedType.addPage()}" />
+                    </dd>
+                </ui:repeat>
+            </s:fragment>
+        </s:fragment>
+        <s:fragment rendered="#{identity.hasPermission('management_groups', 'view') ||
+                    identity.hasPermission('management_template', 'view') ||
+                    identity.hasPermission('management_update', 'view') ||
+                    identity.hasPermission('security', 'view')}">
+            <dt>Other operations:</dt>
+            <hr />
+            <s:fragment rendered="#{identity.hasPermission('management_groups', 'view')}">
+                <dd><s:link value="Manage feed groups" view="/manage/group/group_list.xhtml" /></dd>
+            </s:fragment>
+            <s:fragment rendered="#{identity.hasPermission('management_template', 'view')}">
+                <dd><s:link value="Manage feed templates" view="/manage/template/template_list.xhtml" /></dd>
+            </s:fragment>
+            <s:fragment rendered="#{identity.hasPermission('management_update', 'view')}">
+                <dd><s:link value="Manage updates" view="/manage/update_manager.xhtml" /></dd>
+            </s:fragment>
+            <s:fragment rendered="#{identity.hasPermission('management_configuration', 'view')}">
+                <dd><s:link value="Manage configuration" view="/manage/configuration_manager.xhtml" /></dd>
+            </s:fragment>
+            <s:fragment rendered="#{identity.hasPermission('security', 'view')}">
+                <dd><s:link value="Manage security" view="/security/security_manager.xhtml" /></dd>
+            </s:fragment>
+        </s:fragment>
+        <s:fragment rendered="#{identity.hasPermission('admin', null)}">
+            <dt>Global posts operations:</dt>
+            <hr />
+            <dd><s:link value="Fix html in all posts" action="#{adminBean.fixHtml}" /></dd>
+            <dd><s:link value="Remove duplicates" action="#{adminBean.removeDuplicates}" /></dd>
+            <dd><s:link value="Update missing templates" action="#{adminBean.updateMissingTemplates}" /></dd>
+            <dd><s:link value="Re-index posts (for search)" action="#{postSearch.reindex}" /></dd>
+        </s:fragment>
+        <dt>Existing feed operations:</dt>
+        <hr />
+    </dl>
+</div>
+
+<table border="0" width="100%" cellpadding="0" cellspacing="0" class="basetablestyle" style="margin-top:12px;">
+    <tr class="header">
+        <td class="tableheaderfirst" style="width:160px;">Feed title</td>
+        <td class="tableheader">Feed name</td>
+        <td class="tableheader">Feed type</td>
+        <td class="tableheader">Edit common feed properties</td>
+        <td class="tableheader">Edit feed-type-specific properties</td>
+        <td class="tableheader">Delete the feed</td>
+    </tr>
+
+    <ui:repeat var="group" value="#{groupsService.allGroups}">
+        <mamut:let var="acceptedFeeds" value="#{groupsService.acceptedFeeds(group)}">
+            <s:fragment rendered="#{acceptedFeeds.size() > 0 and
+                    identity.hasPermission('management_group', 'view', group, acceptedFeeds)}">
+                <tr>
+                    <td colspan="7" class="categoryRow">#{group.displayName}</td>
+                </tr>
+
+                <a:repeat var="feed" value="#{acceptedFeeds}" rowKeyVar="rowNumber">
+                    <s:fragment rendered="#{identity.hasPermission('feed', 'edit', feed, group) ||
+                        identity.hasPermission('feed', 'delete', feed, group)}">
+                        <tr class="#{(rowNumber%2 == 0) ? 'evenRow' : 'oddRow'}">
+                            <td class="rowlinefirst" style="font-weight:bold;">#{feed.title}</td>
+                            <td class="rowline">#{feed.name}</td>
+                            <td class="rowline">#{feedTypes.getFeedType(feed).name()}</td>
+                            <td class="rowline">
+                                <s:link view="/manage/feed_edit.xhtml" value="Edit common"
+                                        rendered="#{identity.hasPermission('feed', 'edit', feed, group)}">
+                                    <f:param name="name" value="#{feed.name}" />
+                                </s:link>
+                            </td>
+                            <td class="rowline">
+                                <s:link view="#{feedTypes.getFeedType(feed).editPage()}" value="Edit specific"
+                                        rendered="#{identity.hasPermission('feed', 'edit', feed, group)}">
+                                    <f:param name="name" value="#{feed.name}" />
+                                </s:link>
+                            </td>
+                            <td class="rowline">
+                                <s:link view="/manage/feed_delete.xhtml" action="#{feedMod.delete}" value="Delete"
+                                        onclick="if (!confirm('Are you sure you want to delete this feed?')) return false"
+                                        rendered="#{identity.hasPermission('feed', 'delete', feed, group)}">
+                                    <f:param name="name" value="#{feed.name}" />
+                                </s:link>
+                            </td>
+                        </tr>
+                    </s:fragment>
+                </a:repeat>
+            </s:fragment>
+        </mamut:let>
+        <mamut:let var="restrictedFeeds" value="#{feedsSecurity.filterViewableFeeds(groupsService.restrictedFeeds(group))}">            
+            <s:fragment rendered="#{restrictedFeeds.size() > 0 and
+                    identity.hasPermission('management_group', 'view', group, restrictedFeeds)}">
+                <tr>
+                    <td colspan="7" class="categoryRow">#{group.displayName} (restricted)</td>
+                </tr>
+
+                <a:repeat var="feed" value="#{restrictedFeeds}">
+                    <s:fragment rendered="#{(identity.hasPermission('feed', 'edit', feed, group) ||
+                        identity.hasPermission('feed', 'delete', feed, group)) and
+                        identity.hasPermission('feed', 'view', feed)}">
+                        <tr class="evenRow">
+                            <td class="rowlinefirst" style="font-weight:bold;">#{feed.title} (restricted)</td>
+                            <td class="rowline">#{feed.name}</td>
+                            <td class="rowline">#{feedTypes.getFeedType(feed).name()}</td>
+                            <td class="rowline">
+                                <s:link view="/manage/feed_edit.xhtml" value="Edit common"
+                                        rendered="#{identity.hasPermission('feed', 'edit', feed, group)}">
+                                    <f:param name="name" value="#{feed.name}" />
+                                </s:link>
+                            </td>
+                            <td class="rowline">
+                                <s:link view="#{feedTypes.getFeedType(feed).editPage()}" value="Edit specific"
+                                        rendered="#{identity.hasPermission('feed', 'edit', feed, group)}">
+                                    <f:param name="name" value="#{feed.name}" />
+                                </s:link>
+                            </td>
+                            <td class="rowline">
+                                <s:link view="/manage/feed_delete.xhtml" action="#{feedMod.delete}" value="Delete"
+                                        onclick="if (!confirm('Are you sure you want to delete this feed?')) return false"
+                                        rendered="#{identity.hasPermission('feed', 'delete', feed, group)}">
+                                    <f:param name="name" value="#{feed.name}" />
+                                </s:link>
+                            </td>
+                        </tr>
+                    </s:fragment>
+                </a:repeat>
+            </s:fragment>
+        </mamut:let>
+    </ui:repeat>
+</table>
+</ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/individual/individual_add.xhtml
===================================================================
--- feeds100P26/view/manage/individual/individual_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/individual/individual_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,21 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Add a new individual posts feed - edit common data
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="../feed_mod.xhtml">
+            <ui:param name="new" value="true" />
+            <ui:param name="advanced" value="true" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/individual/individual_edit.xhtml
===================================================================
--- feeds100P26/view/manage/individual/individual_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/individual/individual_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,66 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit individual posts feed: #{feedMod.feed.name}
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="blogFeedNav #{additionalStyle}">
+            <ul>
+                <ui:include src="../../common/next_previous_navigation.xhtml">
+                    <ui:param name="viewId" value="/manage/individual/individual_edit.xhtml" />
+                    <ui:param name="navigationBean" value="#{individualFeedMod}" />
+                    <ui:param name="showColon" value="false" />
+                </ui:include>
+            </ul>
+        </div>
+
+        <h:form>
+            <s:div rendered="#{individualFeedMod.individualPostsFeed.postInfos.size == 0}">
+                There are no posts included.
+            </s:div>
+
+            <s:div id="postsTable">
+                <table class="basetablestyle" >
+                    <a:repeat rowKeyVar="rowNumber" var="postInfo"
+                              value="#{individualFeedMod.postInfos}" rows="#{individualFeedMod.postsOnPage}">
+                        <tr class="#{(rowNumber%2 == 0) ? 'evenRow' : 'oddRow'}">
+                            <td>
+                                <ui:include src="../../common/post.xhtml">
+                                    <ui:param name="post" value="#{postInfo.post}" />
+                                    <ui:param name="showSummary" value="true" />
+                                    <ui:param name="showAddToHighlights" value="false" />
+                                </ui:include>
+                            </td>
+                            <td>
+                                <h:commandLink value="Delete" action="#{individualFeedMod.delete(postInfo)}" />
+                            </td>
+                        </tr>
+                    </a:repeat>
+                </table>
+            </s:div>
+
+            <s:div styleClass="formbuttons">
+                <ul>
+                    <li>
+                        <s:button value="Return" view="/manage/index.xhtml" propagation="end" styleClass="submit" />
+                    </li>
+                    <li>
+                        <s:button value="Add a new post" view="/manage/individual/post_add.xhtml" styleClass="submit" />
+                    </li>
+                    <li>
+                        <ui:include src="../../common/ajax_status.xhtml" />
+                    </li>
+                </ul>
+            </s:div>
+        </h:form>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/individual/post_add.xhtml
===================================================================
--- feeds100P26/view/manage/individual/post_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/individual/post_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,91 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit individual posts feed: #{feedMod.feed.name}
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="adminforms">
+            <h:form>
+                <h:panelGrid columns="2" id="form_data">
+                    <h:outputLabel><span class="required">*</span> Remote feed (atom/rss2) address:</h:outputLabel>
+                    <h:panelGroup>
+                        <h:inputText id="address" value="#{individualFeedMod.address}" required="true" size="55"
+                                     maxlength="64" disabled="#{individualFeedMod.parseOk}">
+                            <s:validate />
+                        </h:inputText>
+                        <a:outputPanel id="addressMessage">
+                            <h:message for="address" styleClass="error" />
+                        </a:outputPanel>
+                    </h:panelGroup>
+
+                    <h:panelGroup />
+                    <h:panelGroup id="parseStatus">
+                        <h:message for="parse" infoClass="info" warnClass="error" errorClass="error" fatalClass="error" />
+                    </h:panelGroup>
+
+                    <h:outputLabel rendered="#{individualFeedMod.parseOk}">
+                        <span class="required">*</span> Select post to include:
+                    </h:outputLabel>
+                    <h:panelGroup rendered="#{individualFeedMod.parseOk}">
+                        <h:selectOneMenu id="selectPost" required="true" value="#{individualFeedMod.selectedPostTitleAsId}"
+                                         style="width: 500px">
+                            <f:selectItems value="#{individualFeedMod.posts}" />
+                            <s:validate />
+                            <a:support event="onchange" reRender="postAuthorEdit,selectPostMessage"
+                                       action="#{individualFeedMod.updateSelectedPost}" />
+                        </h:selectOneMenu>
+                        <a:outputPanel id="selectPostMessage">
+                            <h:message for="selectPost" styleClass="error" />
+                        </a:outputPanel>
+                    </h:panelGroup>
+
+                    <h:outputLabel rendered="#{individualFeedMod.parseOk}">
+                        <span class="required">*</span> Post author:
+                    </h:outputLabel>
+                    <h:panelGroup rendered="#{individualFeedMod.parseOk}" id="postAuthorEdit">
+                        <h:inputText id="postAuthor" value="#{individualFeedMod.postAuthor}" required="true" size="55"
+                                     maxlength="64">
+                            <s:validate />
+                        </h:inputText>
+                        <a:outputPanel id="postAuthorMessage">
+                            <h:message for="postAuthor" styleClass="error" />
+                        </a:outputPanel>
+                    </h:panelGroup>
+                </h:panelGrid>
+
+                <s:div id="proceed" styleClass="formbuttons">
+                    <ul>
+                        <s:fragment rendered="#{!individualFeedMod.parseOk}">
+                            <li>
+                                <a:commandButton action="#{individualFeedMod.parseFeed}" value="Read and parse the feed"
+                                                 styleClass="submit" id="parse"
+                                                 reRender="form_data,proceed" />
+                            </li>
+                        </s:fragment>
+                        <s:fragment rendered="#{individualFeedMod.parseOk}">
+                            <li>
+                                <h:commandButton value="Add post" action="#{individualFeedMod.addPost}"
+                                                 styleClass="submit" />
+                            </li>
+                        </s:fragment>
+                        <li>
+                            <s:button value="Cancel" action="#{individualFeedMod.reset}" styleClass="submit" />
+                        </li>
+                        <li>
+                            <ui:include src="../../common/ajax_status.xhtml" />
+                        </li>
+                    </ul>
+                </s:div>
+            </h:form>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/proposition/proposition_accept_1.xhtml
===================================================================
--- feeds100P26/view/manage/proposition/proposition_accept_1.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/proposition/proposition_accept_1.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,35 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Accept feed: #{feedMod.feed.name}, remote feed-type-specific settings
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="TwoColumnBlogSubnav">
+            <h4>Tips</h4>
+            <ul>
+                <li>
+                    To see which posts will be saved, click "Read and parse the feed". You can also skip this step
+                    by clicking "Save only 'Post author' and 'Include category'".
+                </li>
+                <li class="last">
+                    #{messages['blog.feed.remote.mod.authors']}
+                </li>
+            </ul>
+        </div>
+
+        <ui:include src="../remote/remote_mod.xhtml">
+            <ui:param name="new" value="false" />
+            <ui:param name="showCaptcha" value="false" />
+            <ui:param name="backTo" value="/manage/proposition/proposition_list.xhtml" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/proposition/proposition_accept_2.xhtml
===================================================================
--- feeds100P26/view/manage/proposition/proposition_accept_2.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/proposition/proposition_accept_2.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Accept feed: #{feedMod.feed.name}, common settings
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="../feed_mod.xhtml">
+            <ui:param name="new" value="false" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/proposition/proposition_list.xhtml
===================================================================
--- feeds100P26/view/manage/proposition/proposition_list.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/proposition/proposition_list.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,68 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Manage feed propositions
+    </ui:define>
+
+    <ui:define name="body">
+        <table border="0" width="100%" cellpadding="0" cellspacing="0" class="basetablestyle" style="margin-top:12px;">
+            <tr class="header">
+                <td class="tableheaderfirst" style="width:160px;">Feed title</td>
+                <td class="tableheader">Feed name</td>
+                <td class="tableheader">Feed address</td>
+                <td class="tableheader">Feed category regexp</td>
+                <td class="tableheader">Feed author email</td>
+                <td class="tableheader">Accept the feed</td>
+                <td class="tableheader">Delete the feed</td>
+            </tr>
+
+            <ui:repeat var="group" value="#{groupsService.allGroups}">
+                <s:fragment rendered="#{identity.hasPermission('management_group', 'view', group, groupsService.acceptedFeeds(group))}">
+                    <tr>
+                        <td colspan="7" class="categoryRow">#{group.displayName}</td>
+                    </tr>
+
+                    <s:fragment rendered="#{groupsService.unacceptedFeeds(group).size() == 0}">
+                        <tr>
+                            <td colspan="7">There are no pending feed propositions.</td>
+                        </tr>
+                    </s:fragment>
+
+                    <a:repeat var="feed" value="#{groupsService.unacceptedFeeds(group)}" rowKeyVar="rowNumber">
+                        <s:fragment rendered="#{identity.hasPermission('feed', 'edit', feed, group) ||
+                            identity.hasPermission('feed', 'delete', feed, group)}">
+                            <tr class="#{(rowNumber%2 == 0) ? 'evenRow' : 'oddRow'}">
+                                <td class="rowlinefirst" style="font-weight:bold;">#{feed.title}</td>
+                                <td class="rowline">#{feed.name}</td>
+                                <td class="rowline"><h:outputLink value="#{feed.remoteLink}">#{feed.remoteLink}</h:outputLink></td>
+                                <td class="rowline">#{feed.includeCategoryRegexp}</td>
+                                <td class="rowline">#{propositionsTools.getEmailForProposedFeed(feed)}</td>
+                                <td class="rowline">
+                                    <s:link view="/manage/proposition/proposition_accept_1.xhtml" value="Accept"
+                                            rendered="#{identity.hasPermission('feed', 'edit', feed, group)}">
+                                        <f:param name="name" value="#{feed.name}" />
+                                    </s:link>
+                                </td>
+                                <td class="rowline">
+                                    <s:link view="/manage/feed_delete.xhtml" action="#{feedMod.delete}" value="Delete"
+                                            onclick="if (!confirm('Are you sure you want to delete this feed?')) return false"
+                                            rendered="#{identity.hasPermission('feed', 'delete', feed, group)}">
+                                        <f:param name="name" value="#{feed.name}" />
+                                    </s:link>
+                                </td>
+                            </tr>
+                        </s:fragment>
+                    </a:repeat>
+                </s:fragment>
+            </ui:repeat>
+        </table>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/remote/remote_add.xhtml
===================================================================
--- feeds100P26/view/manage/remote/remote_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/remote/remote_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,35 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Add remote feed
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="TwoColumnBlogSubnav">
+            <h4>Tips</h4>
+            <ul>
+                <li>
+                    #{messages['blog.feed.remote.adding.quickstart']}
+                </li>
+                <li class="last">
+                    #{messages['blog.feed.remote.mod.authors']}
+                </li>
+            </ul>
+        </div>
+
+        <ui:include src="remote_mod.xhtml">
+            <ui:param name="new" value="true" />
+            <ui:param name="inlineTips" value="false" />
+            <ui:param name="showCaptcha" value="false" />
+            <ui:param name="backTo" value="/manage/index.html" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/remote/remote_edit.xhtml
===================================================================
--- feeds100P26/view/manage/remote/remote_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/remote/remote_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,36 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit remote feed: #{feedMod.feed.name}
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="TwoColumnBlogSubnav">
+            <h4>Tips</h4>
+            <ul>
+                <li>
+                    You can change the address of your feed, however use this option with caution; if you
+                    have a completely new feed, maybe it's better to create a new remote feed instead?
+                </li>
+                <li class="last">
+                    #{messages['blog.feed.remote.mod.authors']}
+                </li>
+            </ul>
+        </div>
+
+        <ui:include src="remote_mod.xhtml">
+            <ui:param name="new" value="false" />
+            <ui:param name="inlineTips" value="false" />
+            <ui:param name="showCaptcha" value="false" />
+            <ui:param name="backTo" value="/manage/index.html" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/remote/remote_mod.xhtml
===================================================================
--- feeds100P26/view/manage/remote/remote_mod.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/remote/remote_mod.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,156 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+<div class="adminforms">
+<h:form>
+<h:panelGrid columns="2">
+    <h:panelGroup rendered="#{showCaptcha}">
+        <span class="required">*</span>
+        <h:graphicImage value="/seam/resource/captcha/#{captchaTools.id}" id="captchaGraphic"/>
+    </h:panelGroup>
+    <h:panelGroup rendered="#{showCaptcha}">
+        <h:inputText id="verifyCaptcha" value="#{captcha.response}" required="true">
+            <s:validate />
+        </h:inputText>
+        <a:outputPanel id="captchaMessage">
+            <h:message for="verifyCaptcha" styleClass="error" />
+        </a:outputPanel>
+    </h:panelGroup>
+
+    <h:outputLabel><span class="required">*</span> Remote feed (atom/rss2) address:</h:outputLabel>
+    <h:panelGroup id="addressBody">
+        <h:inputText id="link" value="#{remoteFeedMod.remoteFeed.remoteLink}" required="true" size="55"
+                     maxlength="64" disabled="#{remoteFeedMod.parseOk}">
+            <s:validate />
+        </h:inputText>
+        <a:outputPanel id="linkMessage">
+            <h:message for="link" styleClass="error" />
+        </a:outputPanel>
+    </h:panelGroup>
+
+    <h:panelGroup rendered="#{inlineTips}" />
+    <h:panelGroup rendered="#{inlineTips}" styleClass="instructional_text">
+        <p>
+            #{messages['blog.feed.remote.adding.quickstart']}
+            After submitting, your feed will be added to the propositions queue and reviewed.
+            <br /><br />
+            If you are blogging on a wider area of subjects, please submit a feed of only jboss-related
+            entries (for example a feed of one category/ tag), or, if category information is included
+            in the entries of the feed, select a category after parsing the feed.
+        </p>
+    </h:panelGroup>
+
+    <h:outputLabel for="postAuthorType"><span class="required">*</span> How to determine posts authors:</h:outputLabel>
+    <h:panelGroup>
+        <h:selectOneMenu id="postAuthorType" value="#{remoteFeedMod.remoteFeed.postAuthorType}"
+                         required="true" styleClass="selectwide">
+            <s:enumItem enumValue="BLOG_AUTHOR_IF_MISSING" label="Overwrite with blog author when post
+                            author is missing" />
+            <s:enumItem enumValue="POST_AUTHOR" label="Always use original post author" />
+            <s:enumItem enumValue="BLOG_AUTHOR" label="Always overwrite post author with blog author" />
+            <s:convertEnum />
+            <s:validate />
+        </h:selectOneMenu>
+    </h:panelGroup>
+
+    <h:panelGroup rendered="#{inlineTips}" />
+    <h:panelGroup rendered="#{inlineTips}" styleClass="instructional_text">
+        <p>#{messages['blog.feed.remote.mod.authors']}</p>
+    </h:panelGroup>
+
+    <h:outputLabel id="includeCategoryHeader">
+        <s:fragment rendered="#{(remoteFeedMod.parseOk or !new) and (remoteFeedMod.includeCategories.size() > 2)}">
+            <span class="required">*</span> Include only posts from category:
+        </s:fragment>
+    </h:outputLabel>
+    <h:panelGroup id="includeCategoryBody">
+        <s:fragment rendered="#{(remoteFeedMod.parseOk or !new) and (remoteFeedMod.includeCategories.size() > 2)}">
+            <h:selectOneMenu id="includeCategory" required="true" value="#{remoteFeedMod.includeCategory}"
+                             style="width: 200px">
+                <f:selectItems value="#{remoteFeedMod.includeCategories}" />
+                <s:validate />
+                <a:support event="onchange" reRender="includeCategoryBody,postsIncluded" ajaxSingle="true"
+                        action="#{remoteFeedMod.generateVisiblePosts}" />
+            </h:selectOneMenu>
+            <h:inputText id="includeOtherCategory" rendered="#{remoteFeedMod.includeCategory == 'OTHER'}"
+                         value="#{remoteFeedMod.includeOtherCategoryRegexp}" required="true">
+                <s:validate />
+                <a:support event="onblur" reRender="includeCategoryMessage,postsIncluded" ajaxSingle="true"
+                        action="#{remoteFeedMod.generateVisiblePosts}" />
+            </h:inputText>
+            <a:outputPanel id="includeCategoryMessage">
+                <h:message for="includeCategory" styleClass="error" />
+                <h:message for="includeOtherCategory" styleClass="error" />
+            </a:outputPanel>
+        </s:fragment>
+    </h:panelGroup>
+
+    <h:panelGroup />
+    <h:panelGroup id="parseStatus">
+        <h:message for="parse" infoClass="info" warnClass="error" errorClass="error" fatalClass="error" />
+    </h:panelGroup>
+</h:panelGrid>
+
+<s:div id="proceed" styleClass="formbuttons">
+    <ul>
+        <s:fragment rendered="#{!remoteFeedMod.parseOk}">
+            <li>
+                <a:commandButton action="#{remoteFeedMod.parseFeed}" value="Read and parse the feed"
+                                 styleClass="submit" id="parse"
+                                 reRender="parseStatus,proceed,linkMessage,link,captchaGraphic,captchaMessage,postAuthorType,includeCategoryHeader,includeCategoryBody,addressBody,postsIncluded" />
+            </li>
+        </s:fragment>
+        <s:fragment rendered="#{remoteFeedMod.parseOk and new}">
+            <li>
+                <h:commandButton value="Next &#187;" action="#{remoteFeedMod.saveNew}"
+                                 styleClass="submit" />
+            </li>
+        </s:fragment>
+        <s:fragment rendered="#{remoteFeedMod.parseOk and !new}">
+            <li>
+                <h:commandButton value="Save" action="#{remoteFeedMod.saveExisting}"
+                                 styleClass="submit" />
+            </li>
+        </s:fragment>
+        <s:fragment rendered="#{!remoteFeedMod.parseOk and !new}">
+            <li>
+                <h:commandButton value="Save only 'Post author' and 'Include category'" action="#{remoteFeedMod.savePartial}"
+                                 styleClass="submit" />
+            </li>
+        </s:fragment>
+        <li>
+            <s:button value="Cancel" view="#{backTo}" propagation="end" styleClass="submit" />
+        </li>
+        <li>
+            <ui:include src="../../common/ajax_status.xhtml" />
+        </li>
+    </ul>
+</s:div>
+
+<s:div id="postsIncluded">
+    <s:fragment rendered="#{remoteFeedMod.parseOk}">
+        <h4>
+            <s:fragment rendered="#{new}">
+                The following posts will be saved initially:
+            </s:fragment>
+            <s:fragment rendered="#{!new}">
+                The following posts will be merged into current posts on the next update, after saving:
+            </s:fragment>
+        </h4>
+
+        <ul>
+            <ui:repeat var="post" value="#{remoteFeedMod.visiblePosts}">
+                <li><a href="#{post.link}">#{post.title}</a></li>
+            </ui:repeat>
+        </ul>
+    </s:fragment>
+</s:div>
+</h:form>
+</div>
+</ui:composition>

Added: feeds100P26/view/manage/remote/remote_propose.xhtml
===================================================================
--- feeds100P26/view/manage/remote/remote_propose.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/remote/remote_propose.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,23 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Propose a blog
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="remote_mod.xhtml">
+            <ui:param name="new" value="true" />
+            <ui:param name="inlineTips" value="true" />
+            <ui:param name="showCaptcha" value="true" />
+            <ui:param name="backTo" value="/home.xhtml" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/shotoku/shotoku_add.page.xml
===================================================================
--- feeds100P26/view/manage/shotoku/shotoku_add.page.xml	                        (rev 0)
+++ feeds100P26/view/manage/shotoku/shotoku_add.page.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,6 @@
+<page view-id="/manage/shotoku/shotoku_add.xhtml" login-required="true">
+    <begin-conversation flush-mode="manual" join="true" />
+    <navigation from-action="#{shotokuFeedMod.saveNew}">
+        <redirect view-id="/manage/feed_add.xhtml" />
+    </navigation>
+</page>

Added: feeds100P26/view/manage/shotoku/shotoku_add.xhtml
===================================================================
--- feeds100P26/view/manage/shotoku/shotoku_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/shotoku/shotoku_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Add a Shotoku feed
+    </ui:define>
+    
+    <ui:define name="body">
+        <ui:include src="shotoku_mod.xhtml">
+            <ui:param name="new" value="true" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/shotoku/shotoku_edit.page.xml
===================================================================
--- feeds100P26/view/manage/shotoku/shotoku_edit.page.xml	                        (rev 0)
+++ feeds100P26/view/manage/shotoku/shotoku_edit.page.xml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,9 @@
+<page view-id="/manage/shotoku/shotoku_edit.xhtml" login-required="true">
+    <begin-conversation flush-mode="manual" join="true" />
+    <param name="name" converterId="feedConverter" value="#{feedMod.feed}" />
+    <restrict>#{identity.hasPermission('feed', 'edit', feedMod.feed, feedMod.feed.group)}</restrict>
+    <navigation from-action="#{shotokuFeedMod.saveExisting}">
+        <end-conversation />
+        <redirect view-id="/manage/index.xhtml" />
+    </navigation>
+</page>
\ No newline at end of file

Added: feeds100P26/view/manage/shotoku/shotoku_edit.xhtml
===================================================================
--- feeds100P26/view/manage/shotoku/shotoku_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/shotoku/shotoku_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit shotoku feed: #{feedMod.feed.name}
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="shotoku_mod.xhtml">
+            <ui:param name="new" value="false" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/shotoku/shotoku_mod.xhtml
===================================================================
--- feeds100P26/view/manage/shotoku/shotoku_mod.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/shotoku/shotoku_mod.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,153 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+<div class="TwoColumnBlogSubnav">
+    <h4>Tips</h4>
+    <ul>
+        <li>
+                You can convert files from a directory in SVN to posts using Shotoku.
+                The conversion is as follows:
+                <br />
+                <br />
+                <i>post title</i> - value of the 'title' property of the file
+                <br />
+                <br />
+                <i>post author</i> - value of the 'author' property; however if all posts have the
+                same author, you can omit this, then the value of the author field for the feed will be used
+                <br />
+                <br />
+                <i>post content</i> - file content
+        </li>
+        <li>
+                Podcasts are also easy to create. The conversion is a little bit different:
+                <br />
+                <br />
+                <i>post content</i> - value of the 'description' property
+                <br />
+                <br />
+                <i>post enclosure</i> - the file forms the enclosure; however you need to provide
+                the link, from which this file can be downloaded, by providing a relative URL directory, to
+                which the name of the file will be appended
+                <br />
+                <br />
+                <i>post image (optional)</i> - value of the 'thumbnail' property (also a relative URL)
+        </li>
+        <li id="last">
+            #{messages['blog.feed.remote.mod.authors']}
+        </li>
+    </ul>
+</div>
+
+<div class="adminforms">
+<h:form>
+<h:panelGrid columns="2">
+    <h:outputLabel><span class="required">*</span> Content manager id:</h:outputLabel>
+    <h:panelGroup>
+        <h:inputText id="cmId" value="#{shotokuFeedMod.shotokuFeed.cmId}" required="true" size="55"
+                     maxlength="64">
+            <s:validate />
+        </h:inputText>
+        <a:outputPanel id="cmIdMessage">
+            <h:message for="cmId" styleClass="error" />
+        </a:outputPanel>
+    </h:panelGroup>
+
+    <h:outputLabel><span class="required">*</span> Path:</h:outputLabel>
+    <h:panelGroup>
+        <h:inputText id="cmPath" value="#{shotokuFeedMod.shotokuFeed.cmPath}" required="true" size="55"
+                     maxlength="64">
+            <s:validate />
+        </h:inputText>
+        <a:outputPanel id="cmPathMessage">
+            <h:message for="cmPath" styleClass="error" />
+        </a:outputPanel>
+    </h:panelGroup>
+
+    <h:outputLabel>This is a podcast:</h:outputLabel>
+    <h:panelGroup>
+        <h:selectBooleanCheckbox id="podcast" value="#{shotokuFeedMod.podcast}" required="true">
+            <a:support event="onchange" reRender="podcastPrefix" ajaxSingle="true"
+                       bypassUpdates="false"/>
+            <s:validate />
+        </h:selectBooleanCheckbox>
+        <a:outputPanel id="podcastMessage">
+            <h:message for="podcast" styleClass="error" />
+        </a:outputPanel>
+    </h:panelGroup>
+
+    <h:outputLabel>
+        URL prefix for podcast files:
+    </h:outputLabel>
+    <h:panelGroup>
+        <h:inputText id="podcastPrefix" value="#{shotokuFeedMod.shotokuFeed.podcastPrefix}"
+                     disabled="#{!shotokuFeedMod.podcast}" required="#{shotokuFeedMod.podcast}">
+            <s:validate />
+        </h:inputText>
+        <a:outputPanel id="podcastPrefixMessage">
+            <h:message for="podcastPrefix" styleClass="error" />
+        </a:outputPanel>
+    </h:panelGroup>
+
+    <h:outputLabel for="postAuthorType"><span class="required">*</span> Post author:</h:outputLabel>
+    <h:panelGroup>
+        <h:selectOneMenu id="postAuthorType" value="#{shotokuFeedMod.shotokuFeed.postAuthorType}"
+                         required="true" styleClass="selectwide">
+            <s:enumItem enumValue="BLOG_AUTHOR_IF_MISSING" label="Overwrite with blog author when post
+                            author is missing" />
+            <s:enumItem enumValue="POST_AUTHOR" label="Always use original post author" />
+            <s:enumItem enumValue="BLOG_AUTHOR" label="Always overwrite post author with blog author" />
+            <s:convertEnum />
+            <s:validate />
+        </h:selectOneMenu>
+    </h:panelGroup>
+
+    <h:panelGroup />
+    <h:panelGroup id="checkStatus">
+        <h:panelGroup rendered="#{shotokuFeedMod.pathOk}">
+            Checking the path was successfull! You can proceed.
+        </h:panelGroup>
+        <h:panelGroup rendered="#{!shotokuFeedMod.pathOk and shotokuFeedMod.pathException != null}">
+            Checking the path failed, because of the following exception:
+            #{shotokuFeedMod.pathException.message}
+        </h:panelGroup>
+    </h:panelGroup>
+</h:panelGrid>
+
+<s:div id="proceed" styleClass="formbuttons">
+    <ul>
+        <s:fragment rendered="#{!shotokuFeedMod.pathOk}">
+            <li>
+                <a:commandButton action="#{shotokuFeedMod.checkPath}" value="Check the path"
+                                 styleClass="submit"
+                                 reRender="checkStatus,proceed,cmId,cmIdMessage,cmPath,cmPathMessage,podcast,podcastMessage,podcastPrefix,podcastPrefixMessage" />
+            </li>
+        </s:fragment>
+        <s:fragment rendered="#{shotokuFeedMod.pathOk and new}">
+            <li>
+                <h:commandButton value="Next &#187;" action="#{shotokuFeedMod.saveNew}"
+                                 styleClass="submit" />
+            </li>
+        </s:fragment>
+        <s:fragment rendered="#{shotokuFeedMod.pathOk and !new}">
+            <li>
+                <h:commandButton value="Save" action="#{shotokuFeedMod.saveExisting}"
+                                 styleClass="submit" />
+            </li>
+        </s:fragment>
+        <li>
+            <s:button value="Cancel" view="/manage/index.xhtml" propagation="end" styleClass="submit" />
+        </li>
+        <li>
+            <ui:include src="../../common/ajax_status.xhtml" />
+        </li>
+    </ul>
+</s:div>
+</h:form>
+</div>
+</ui:composition>

Added: feeds100P26/view/manage/template/template_add.xhtml
===================================================================
--- feeds100P26/view/manage/template/template_add.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/template/template_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Add a new template
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="template_mod.xhtml">
+            <ui:param name="new" value="true" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/template/template_edit.xhtml
===================================================================
--- feeds100P26/view/manage/template/template_edit.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/template/template_edit.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,20 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Edit template: #{templateMod.template.name}
+    </ui:define>
+    
+    <ui:define name="body">
+        <ui:include src="template_mod.xhtml">
+            <ui:param name="new" value="false" />
+        </ui:include>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/template/template_list.xhtml
===================================================================
--- feeds100P26/view/manage/template/template_list.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/template/template_list.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,51 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../../layout/template.xhtml">
+    <ui:define name="header">
+        Manage templates
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="adminlist">
+            <dl>
+                <dd><s:link value="Add new template" view="/manage/template/template_add.xhtml" /></dd>
+                <dt>Current templates:</dt>
+                <hr />
+            </dl>
+        </div>
+
+        <table border="0" cellpadding="4" cellspacing="0" class="basetablestyle" style="margin-top:12px;">
+            <tr class="header">
+                <td class="tableheaderfirst" style="width:160px;">Template name</td>
+                <td class="tableheader">Template type</td>
+                <td class="tableheader" />
+                <td class="tableheader" />
+            </tr>
+
+            <a:repeat var="template" value="#{templateService.allTemplates}" rowKeyVar="rowNumber">
+                <tr class="#{(rowNumber%2 == 0) ? 'evenRow' : 'oddRow'}">
+                    <td class="rowlinefirst" style="font-weight:bold;">#{template.name}</td>
+                    <td class="rowline">#{template.type}</td>
+                    <td class="rowline">
+                        <s:link view="/manage/template/template_edit.xhtml" value="Edit">
+                            <f:param name="id" value="#{template.id}" />
+                        </s:link>
+                    </td>
+                    <td class="rowline">
+                        <s:link view="/manage/template/template_delete.xhtml" action="#{templateMod.delete}" value="Delete"
+                                onclick="if (!confirm('Are you sure you want to delete this template?')) return false">
+                            <f:param name="id" value="#{template.id}" />
+                        </s:link>
+                    </td>
+                </tr>
+            </a:repeat>
+        </table>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/manage/template/template_mod.xhtml
===================================================================
--- feeds100P26/view/manage/template/template_mod.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/template/template_mod.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,104 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:blog="http://jboss.org/blog/tags"
+                xmlns:a="http://richfaces.org/a4j">
+    <div class="adminforms">
+        <h:form>
+            <h:panelGrid columns="2">
+                <h:outputLabel for="name"><span class="required">*</span> Name:</h:outputLabel>
+                <h:panelGroup>
+                    <h:inputText id="name" value="#{templateMod.template.name}" required="true" size="32">
+                        <blog:uniqueTemplateNameValidator entityId="#{templateMod.template.id}" />
+                        <a:support event="onblur" reRender="nameMessage" ajaxSingle="true" bypassUpdates="true"/>
+                        <s:validate />
+                    </h:inputText>
+
+                    <a:outputPanel id="nameMessage">
+                        <h:message for="name" styleClass="error" />
+                    </a:outputPanel>
+                </h:panelGroup>
+
+                <h:outputLabel for="type"><span class="required">*</span> Template type:</h:outputLabel>
+                <h:panelGroup>
+                    <h:selectOneMenu id="type" value="#{templateMod.template.type}" required="true"
+                            styleClass="selectnarrow">
+                        <s:enumItem enumValue="ATOM" />
+                        <s:enumItem enumValue="RSS2" />
+                        <s:convertEnum />
+                        <a:support event="onchange" reRender="typeMessage" ajaxSingle="true" />
+                        <s:validate />
+                    </h:selectOneMenu>
+
+                    <a:outputPanel id="typeMessage">
+                        <h:message for="type" styleClass="error" />
+                    </a:outputPanel>
+                </h:panelGroup>
+
+                <h:outputLabel for="text"><span class="required">*</span> Text:</h:outputLabel>
+                <h:panelGroup>
+                    <h:inputTextarea id="text" value="#{templateMod.template.text}" rows="40" cols="100" required="true">
+                        <a:support event="onblur" reRender="textMessage" ajaxSingle="true" bypassUpdates="true"/>
+                        <s:validate />
+                    </h:inputTextarea>
+
+                    <a:outputPanel id="textMessage">
+                        <h:message for="text" styleClass="error" />
+                    </a:outputPanel>
+                </h:panelGroup>
+            </h:panelGrid>
+
+
+            <div id="proceed" class="formbuttons">
+                <ul>
+                    <s:fragment rendered="#{new}">
+                        <li>
+                            <h:commandButton value="Add" action="#{templateMod.saveNew}" styleClass="submit" />
+                        </li>
+                    </s:fragment>
+                    <s:fragment rendered="#{!new}">
+                        <li>
+                            <h:commandButton value="Save" action="#{templateMod.saveExisting}" styleClass="submit" />
+                        </li>
+                    </s:fragment>
+                    <li>
+                        <s:button value="Cancel" view="/manage/template/template_list.xhtml" propagation="end"
+                                styleClass="submit" />
+                    </li>
+                </ul>
+            </div>
+        </h:form>
+
+        <p>
+            When creating a template, the context contains the following variables:
+        </p>
+        <ul>
+            <li>$feed - the <code>org.jboss.blog.model.Feed</code> object, for which the feed is being generated</li>
+            <li>$posts - a list of <code>org.jboss.blog.model.Post</code> objects, which is the list of posts
+                for the feed</li>
+            <li>$xmlType - the type of the template being generated (one of the enum values
+                <code>org.jboss.blog.model.TemplateType</code>)</li>
+            <li>$tools - a utility object containing the following functions:
+                <ul>
+                    <li>$tools.formatDate(java.util.Date) - formats the date using a format appropriate for this
+                        template type</li>
+                    <li>$tools.feedPubDate(org.jboss.blog.model.Feed, java.util.List&lt;org.jboss.blog.model.Post&gt; -
+                        generates the feed publish date, which is the published date of the newest post</li>
+                    <li>$tools.feedPageLink(org.jboss.blog.model.Feed) - generates a link to the html version of the
+                        given feed</li>
+                    <li>$tools.feedLink(org.jboss.blog.model.Feed, org.jboss.blog.model.TemplateType) - generates
+                        a link to a feed of the given type</li>
+                    <li>$tools.postLink(org.jboss.blog.model.Post) - generates a link to the html version of the
+                        given post</li>
+                    <li>$tools.escape(java.util.String) - escapes special charaters, like &amp;, which can be
+                        found in links</li>
+                </ul>
+            </li>
+        </ul>
+    </div>
+</ui:composition>

Added: feeds100P26/view/manage/update_manager.xhtml
===================================================================
--- feeds100P26/view/manage/update_manager.xhtml	                        (rev 0)
+++ feeds100P26/view/manage/update_manager.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,140 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+<ui:define name="header">
+    Update manager
+</ui:define>
+<ui:define name="body">
+<h:form>
+    <a:poll interval="5000" reRender="mainTable" />
+    <a:outputPanel id="mainTable">
+        <table cellspacing="5" class="deftable" width="75%">
+            <tr>
+                <td class="term" width="15%">Last page refresh:</td>
+                <td class="def">#{updateManager.now}</td>
+            </tr>
+            <tr>
+                <td class="term" width="15%">Last update start:</td>
+                <td class="def">#{updateManager.lastUpdateStartDate}</td>
+            </tr>
+            <tr>
+                <td class="term" width="15%">Last update end:</td>
+                <td class="def">#{updateManager.lastUpdateEndDate}</td>
+            </tr>
+            <tr>
+                <td class="term" width="15%">Update in progress:</td>
+                <td class="def">#{updateManager.updateInProgress.get()}</td>
+            </tr>
+            <tr>
+                <td class="term" width="15%">Global exceptions:</td>
+                <td class="def">
+                    <s:fragment rendered="#{updateManager.globalExceptions.size() == 0}">
+                        None
+                    </s:fragment>
+                    <s:fragment rendered="#{updateManager.globalExceptions.size() > 0}">
+                        <ui:repeat var="exception" value="#{updateManager.globalExceptions}">
+                            <rich:togglePanel id="stackTracePanel" initialState="nothing" switchType="client"
+                                              stateOrder="nothing,stackTrace">
+                                <f:facet name="nothing">
+                                    <s:fragment>
+                                        #{exception.message}
+                                    </s:fragment>
+                                </f:facet>
+                                <f:facet name="stackTrace">
+                                    <s:fragment>
+                                        #{exception.message} <br />
+                                        #{updateManager.getExceptionStackTrace(exception)}
+                                    </s:fragment>
+                                </f:facet>
+                            </rich:togglePanel>
+                            <rich:toggleControl for="stackTracePanel" value="Toggle stack trace"/>
+                        </ui:repeat>
+                    </s:fragment>
+                </td>
+            </tr>
+            <tr>
+                <td class="term" width="15%">Feed exceptions:</td>
+                <td class="def">
+                    <s:fragment rendered="#{updateManager.feedUpdateExceptionNames.size() == 0}">
+                        None
+                    </s:fragment>
+                    <s:fragment rendered="#{updateManager.feedUpdateExceptionNames.size() > 0}">
+                        <ui:repeat var="feedName" value="#{updateManager.feedUpdateExceptionNames}">
+                            <strong>#{feedName}</strong> <br />
+                            <ui:repeat var="exception"
+                                       value="#{updateManager.getFeedUpdateExceptionsForFeed(feedName)}">
+                                <rich:togglePanel id="stackTracePanel" initialState="nothing" switchType="client"
+                                                  stateOrder="nothing,stackTrace">
+                                    <f:facet name="nothing">
+                                        <s:fragment>
+                                            #{exception.message}
+                                        </s:fragment>
+                                    </f:facet>
+                                    <f:facet name="stackTrace">
+                                        <s:fragment>
+                                            #{exception.message} <br />
+                                            #{updateManager.getExceptionStackTrace(exception)}
+                                        </s:fragment>
+                                    </f:facet>
+                                </rich:togglePanel>
+                                <rich:toggleControl for="stackTracePanel" value="Toggle stack trace"/>
+                                <br />
+                            </ui:repeat>
+                            <br />
+                        </ui:repeat>
+                    </s:fragment>
+                </td>
+            </tr>
+            <tr>
+                <td class="term" width="15%">Actions:</td>
+                <td class="def">
+                    <h:commandLink action="#{updateManager.clearGlobalExceptions}"
+                                   value="Clear global exceptions" /><br />
+                    <h:commandLink action="#{updateManager.clearFeedsExceptions}"
+                                   value="Clear feeds exceptions"/><br />
+                    <h:commandLink action="#{updateManager.restartUpdateThread}"
+                                   value="Restart the update thread"/><br />
+                </td>
+            </tr>
+        </table>
+        <div class="formbuttons" />
+    </a:outputPanel>
+</h:form>
+<h:form>
+    <table cellspacing="5" class="deftable" width="75%">
+        <tr>
+            <td class="term" width="15%">Updates interval (in seconds):</td>
+            <td class="def">
+                <h:inputText value="#{updateManager.updateInterval}"/>
+            </td>
+        </tr>
+        <tr>
+            <td class="term" width="15%">Remote feed read timeout (in milliseconds):</td>
+            <td class="def">
+                <h:inputText value="#{updateManager.readTimeout}"/>
+            </td>
+        </tr>
+        <tr>
+            <td class="term" width="15%">Remote feed connection timeout (in milliseconds):</td>
+            <td class="def">
+                <h:inputText value="#{updateManager.connectionTimeout}"/>
+            </td>
+        </tr>
+    </table>
+    <div class="formbuttons">
+        <ul>
+            <li>
+                <h:commandButton value="Save changes" action="#{updateManager.save}" styleClass="submit" />
+            </li>
+        </ul>
+    </div>
+</h:form>
+</ui:define>
+</ui:composition>

Added: feeds100P26/view/search/search.xhtml
===================================================================
--- feeds100P26/view/search/search.xhtml	                        (rev 0)
+++ feeds100P26/view/search/search.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,50 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Search
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="blogContent">
+            <s:div rendered="#{empty postSearch.results}">
+                <p>
+                    Your search for: '#{postSearch.query}' didn't return any results.
+                </p>
+            </s:div>
+
+            <s:div rendered="#{not empty postSearch.results}">
+                <p>
+                    Your search for: '#{postSearch.query}' returned #{postSearch.resultsCount} results.
+                </p>
+
+                <div class="blogFeedNav">
+                    <ul>
+                        <ui:include src="../common/next_previous_navigation.xhtml">
+                            <ui:param name="viewId" value="/search/search.xhtml" />
+                            <ui:param name="navigationBean" value="#{postSearch}" />
+                            <ui:param name="showColon" value="false" />
+                        </ui:include>
+                    </ul>
+                </div>
+
+                <ui:repeat var="result" value="#{postSearch.results}">
+                    <ui:include src="../common/post.xhtml">
+                        <ui:param name="post" value="#{result[1]}" />
+                        <ui:param name="showSummary" value="true" />
+                        <ui:param name="showAddToHighlights" value="false" />
+                        <ui:param name="showPostTo" value="false" />
+                        <ui:param name="additionalHeader"
+                                  value="(#{postSearch.formatScore(result[0])}%)" />
+                    </ui:include>
+                </ui:repeat>
+            </s:div>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/security/account.xhtml
===================================================================
--- feeds100P26/view/security/account.xhtml	                        (rev 0)
+++ feeds100P26/view/security/account.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,44 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Account for user #{identity.username}
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="TwoColumnBlogSubnav">
+            <h4>Tips</h4>
+            <ul>
+                <li class="last">
+                    The security key is used to view restricted feeds, without the need to log in.
+                    Don't distribute it, or addresses to restricted feeds viewed using this key!
+                </li>
+            </ul>
+        </div>
+
+        <div class="adminforms">
+            <h:form>
+                <h:panelGrid columns="2">
+                    <h:outputLabel for="username">Security key:</h:outputLabel>
+                    <h:inputText id="username" value="#{identity.securityUser.restrictedKey}" disabled="true" />
+                </h:panelGrid>
+
+                <div class="formbuttons">
+                    <ul>
+                        <li>
+                            <h:commandButton value="Re-generate the security key"
+                                             action="#{securityUserKeys.generateKeyForCurrentUser()}"
+                                             styleClass="submit" />
+                        </li>
+                    </ul>
+                </div>
+            </h:form>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/security/login.xhtml
===================================================================
--- feeds100P26/view/security/login.xhtml	                        (rev 0)
+++ feeds100P26/view/security/login.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,52 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Login
+    </ui:define>
+
+    <ui:define name="body">
+        <div class="TwoColumnBlogSubnav">
+            <h4>Tips</h4>
+            <ul>
+                <li>
+                    If you don't have an account yet, click the 'Register' link at the top.
+                </li>
+                <li class="last">
+                    If you have registered, but can't login, you may not have a JBoss.ORG account yet.
+                        To get one, just login once to JBoss.ORG (<a href="http://labs.jboss.com/auth">here</a>),
+                        it will be created automatically.
+                </li>
+            </ul>
+        </div>
+        
+        <div class="adminforms">
+            <h:form>
+                <h:panelGrid columns="2">
+                    <h:outputLabel for="username">Username</h:outputLabel>
+                    <h:inputText id="username" value="#{identity.username}"/>
+
+                    <h:outputLabel for="password">Password</h:outputLabel>
+                    <h:inputSecret id="password" value="#{identity.password}"/>
+                </h:panelGrid>
+
+                <div class="formbuttons">
+                    <ul>
+                        <li>
+                            <h:commandButton value="Login" action="#{identity.login}" styleClass="submit" />
+                        </li>
+                        <li>
+                            <s:button value="Cancel" view="/home.xhtml" styleClass="submit" propagation="none" />
+                        </li>
+                    </ul>
+                </div>
+            </h:form>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/security/security_group_add.xhtml
===================================================================
--- feeds100P26/view/security/security_group_add.xhtml	                        (rev 0)
+++ feeds100P26/view/security/security_group_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,39 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Select group to add:
+    </ui:define>
+    <ui:define name="body">
+        <h:form>
+            <div class="adminforms">
+                <br />
+                <h:panelGrid columns="1">
+                    <h:selectOneListbox required="true" value="#{securityMod.restrictedSecurityGroup}"
+                                        converter="securityGroupConverter">
+                        <s:selectItems var="group" value="#{externalSecurityService.allGroups}"
+                                       label="#{externalSecurityService.getDisplayName(group)}" />
+                    </h:selectOneListbox>
+                </h:panelGrid>
+
+                <div class="formbuttons">
+                    <ul>
+                        <li>
+                            <h:commandButton value="Add" styleClass="submit" action="#{securityMod.addSecurityGroup}" />
+                        </li>
+                        <li>
+                            <s:button value="Cancel" view="/security/security_manager.xhtml" styleClass="submit" />
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </h:form>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/security/security_manager.xhtml
===================================================================
--- feeds100P26/view/security/security_manager.xhtml	                        (rev 0)
+++ feeds100P26/view/security/security_manager.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,222 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+<ui:define name="header">
+    Security manager
+</ui:define>
+<ui:define name="body">
+
+<s:fragment rendered="#{identity.hasPermission('admin', null)}">
+    <h3>Administrators:</h3>
+
+    <table cellspacing="5" class="deftable" width="75%">
+        <tr>
+            <td class="term" width="15%" />
+            <td class="def">
+                <ul>
+                    <ui:repeat var="securityGroup" value="#{securityMod.administratorGroups}">
+                        <li>
+                            #{externalSecurityService.getDisplayName(securityGroup)}
+                            (
+                            <s:link value="delete" action="#{securityMod.deleteSecurityGroup}">
+                                <f:param name="role" value="ADMIN" />
+                                <f:param name="securityGroup" value="#{securityGroup.externalId}" />
+                            </s:link>
+                            )
+                        </li>
+                    </ui:repeat>
+
+                    <s:link value="Add user group" view="/security/security_group_add.xhtml">
+                        <f:param name="role" value="ADMIN" />
+                    </s:link>
+                </ul>
+            </td>
+            <td class="def">
+                <ul>
+                    <ui:repeat var="securityUser" value="#{securityMod.administratorUsers}">
+                        <li>
+                            #{externalSecurityService.getDisplayName(securityUser)}
+                            (
+                            <s:link value="delete" action="#{securityMod.deleteSecurityUser}">
+                                <f:param name="role" value="ADMIN" />
+                                <f:param name="securityUser" value="#{securityUser.externalId}" />
+                            </s:link>
+                            )
+                        </li>
+                    </ui:repeat>
+
+                    <s:link value="Add user" view="/security/security_user_add.xhtml">
+                        <f:param name="role" value="ADMIN" />
+                    </s:link>
+                </ul>
+            </td>
+        </tr>
+    </table>
+</s:fragment>
+
+<h3>Feed groups administrators:</h3>
+
+<table cellspacing="5" class="deftable" width="75%">
+    <ui:repeat var="group" value="#{groupsService.allGroups}">
+        <s:fragment rendered="#{identity.hasPermission('management_group', 'view', group)}">
+            <tr>
+                <td class="term" width="15%">#{group.displayName}</td>
+                <td class="def">
+                    <ui:repeat var="securityGroup" value="#{securityMod.getGroupAdministratorGroups(group)}">
+                        <li>
+                            #{externalSecurityService.getDisplayName(securityGroup)}
+                            (
+                            <s:link value="delete" action="#{securityMod.deleteSecurityGroup}">
+                                <f:param name="role" value="GROUP_ADMIN" />
+                                <f:param name="group" value="#{group.id}" />
+                                <f:param name="securityGroup" value="#{securityGroup.externalId}" />
+                            </s:link>
+                            )
+                        </li>
+                    </ui:repeat>
+
+                    <s:link value="Add user group" view="/security/security_group_add.xhtml">
+                        <f:param name="role" value="GROUP_ADMIN" />
+                        <f:param name="group" value="#{group.id}" />
+                    </s:link>
+                </td>
+                <td class="def">
+                    <ui:repeat var="securityUser" value="#{securityMod.getGroupAdministratorUsers(group)}">
+                        <li>
+                            #{externalSecurityService.getDisplayName(securityUser)}
+                            (
+                            <s:link value="delete" action="#{securityMod.deleteSecurityUser}">
+                                <f:param name="role" value="GROUP_ADMIN" />
+                                <f:param name="group" value="#{group.id}" />
+                                <f:param name="securityUser" value="#{securityUser.externalId}" />
+                            </s:link>
+                            )
+                        </li>
+                    </ui:repeat>
+
+                    <s:link value="Add user" view="/security/security_user_add.xhtml">
+                        <f:param name="role" value="GROUP_ADMIN" />
+                        <f:param name="group" value="#{group.id}" />
+                    </s:link>
+                </td>
+            </tr>
+        </s:fragment>
+    </ui:repeat>
+</table>
+
+<h3>Feed administrators:</h3>
+
+<ui:repeat var="group" value="#{groupsService.allGroups}">
+    <s:fragment rendered="#{identity.hasPermission('management_group', 'view', group)}">
+        #{group.displayName}:
+        <table cellspacing="5" class="deftable" width="75%">
+            <ui:repeat var="feed" value="#{feedsSecurity.filterViewableFeeds(groupsService.allAcceptedFeeds(group))}">
+                <tr>
+                    <td class="term" width="15%">#{feed.name}</td>
+                    <td class="def">
+                        <ui:repeat var="securityGroup" value="#{securityMod.getFeedAdministratorGroups(feed)}">
+                            <li>
+                                #{externalSecurityService.getDisplayName(securityGroup)}
+                                (
+                                <s:link value="delete" action="#{securityMod.deleteSecurityGroup}">
+                                    <f:param name="role" value="FEED_ADMIN" />
+                                    <f:param name="feed" value="#{feed.name}" />
+                                    <f:param name="securityGroup" value="#{securityGroup.externalId}" />
+                                </s:link>
+                                )
+                            </li>
+                        </ui:repeat>
+
+                        <s:link value="Add user group" view="/security/security_group_add.xhtml">
+                            <f:param name="role" value="FEED_ADMIN" />
+                            <f:param name="feed" value="#{feed.name}" />
+                        </s:link>
+                    </td>
+                    <td class="def">
+                        <ui:repeat var="securityUser" value="#{securityMod.getFeedAdministratorUsers(feed)}">
+                            <li>
+                                #{externalSecurityService.getDisplayName(securityUser)}
+                                (
+                                <s:link value="delete" action="#{securityMod.deleteSecurityUser}">
+                                    <f:param name="role" value="FEED_ADMIN" />
+                                    <f:param name="feed" value="#{feed.name}" />
+                                    <f:param name="securityUser" value="#{securityUser.externalId}" />
+                                </s:link>
+                                )
+                            </li>
+                        </ui:repeat>
+
+                        <s:link value="Add user" view="/security/security_user_add.xhtml">
+                            <f:param name="role" value="FEED_ADMIN" />
+                            <f:param name="feed" value="#{feed.name}" />
+                        </s:link>
+                    </td>
+                </tr>
+            </ui:repeat>
+        </table>
+    </s:fragment>
+</ui:repeat>
+
+<h3>Feed viewers:</h3>
+
+<ui:repeat var="group" value="#{groupsService.allGroups}">
+    <s:fragment rendered="#{identity.hasPermission('management_group', 'view', group)}">
+        #{group.displayName}:
+        <table cellspacing="5" class="deftable" width="75%">
+            <ui:repeat var="feed" value="#{groupsService.restrictedFeeds(group)}">
+                <s:fragment rendered="#{identity.hasPermission('feed', 'view', feed)}">
+                    <tr>
+                        <td class="term" width="15%">#{feed.name}</td>
+                        <td class="def">
+                            <ui:repeat var="securityGroup" value="#{securityMod.getFeedViewersGroups(feed)}">
+                                <li>
+                                    #{externalSecurityService.getDisplayName(securityGroup)}
+                                    (
+                                    <s:link value="delete" action="#{securityMod.deleteSecurityGroup}">
+                                        <f:param name="role" value="VIEW" />
+                                        <f:param name="feed" value="#{feed.name}" />
+                                        <f:param name="securityGroup" value="#{securityGroup.externalId}" />
+                                    </s:link>
+                                    )
+                                </li>
+                            </ui:repeat>
+
+                            <s:link value="Add user group" view="/security/security_group_add.xhtml">
+                                <f:param name="role" value="VIEW" />
+                                <f:param name="feed" value="#{feed.name}" />
+                            </s:link>
+                        </td>
+                        <td class="def">
+                            <ui:repeat var="securityUser" value="#{securityMod.getFeedViewersUsers(feed)}">
+                                <li>
+                                    #{externalSecurityService.getDisplayName(securityUser)}
+                                    (
+                                    <s:link value="delete" action="#{securityMod.deleteSecurityUser}">
+                                        <f:param name="role" value="VIEW" />
+                                        <f:param name="feed" value="#{feed.name}" />
+                                        <f:param name="securityUser" value="#{securityUser.externalId}" />
+                                    </s:link>
+                                    )
+                                </li>
+                            </ui:repeat>
+
+                            <s:link value="Add user" view="/security/security_user_add.xhtml">
+                                <f:param name="role" value="VIEW" />
+                                <f:param name="feed" value="#{feed.name}" />
+                            </s:link>
+                        </td>
+                    </tr>
+                </s:fragment>
+            </ui:repeat>
+        </table>
+    </s:fragment>
+</ui:repeat>
+</ui:define>
+</ui:composition>

Added: feeds100P26/view/security/security_user_add.xhtml
===================================================================
--- feeds100P26/view/security/security_user_add.xhtml	                        (rev 0)
+++ feeds100P26/view/security/security_user_add.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,66 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        Select user to add:
+    </ui:define>
+    <ui:define name="body">
+        <h:form>
+            <div class="adminforms">
+                Filter: <h:inputText value="#{securityUserSelect.filter}" />
+                <br />
+
+                <div class="formbuttons">
+                    <ul>
+                        <li>
+                            <h:commandButton value="Search" styleClass="submit" action="search">
+                                <f:param name="from" value="0" />
+                            </h:commandButton>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </h:form>
+
+        <div class="blogFeedNav #{additionalStyle}">
+            <ul>
+                <ui:include src="../common/next_previous_navigation.xhtml">
+                    <ui:param name="viewId" value="/security/security_user_add.xhtml" />
+                    <ui:param name="navigationBean" value="#{securityUserSelect}" />
+                    <ui:param name="showColon" value="false" />
+                </ui:include>
+            </ul>
+        </div>
+
+        <h:form>
+            <div class="adminforms">
+                <br />
+                <h:panelGrid columns="1">
+                    <h:selectOneListbox required="true" value="#{securityMod.restrictedSecurityUser}"
+                                        converter="securityUserConverter">
+                        <s:selectItems var="user" value="#{securityUserSelect.users}"
+                                       label="#{externalSecurityService.getDisplayName(user)}" />
+                    </h:selectOneListbox>
+                </h:panelGrid>
+
+                <div class="formbuttons">
+                    <ul>
+                        <li>
+                            <h:commandButton value="Add" styleClass="submit" action="#{securityMod.addSecurityUser}" />
+                        </li>
+                        <li>
+                            <s:button value="Cancel" view="/security/security_manager.xhtml" styleClass="submit" />
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </h:form>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/stylesheet/blog.css
===================================================================
--- feeds100P26/view/stylesheet/blog.css	                        (rev 0)
+++ feeds100P26/view/stylesheet/blog.css	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,496 @@
+.blogFeedNav {
+}
+
+.blogFeedNav_bottom {
+    border: none 0;
+    padding: 0 0 0 10px;
+}
+
+.blogFeedNav_top {
+    border-bottom: 1px solid #8c8f91;
+    padding: 11px 0 5px 10px;
+    margin-bottom: 1em;
+}
+
+.blogFeedNav ul {
+    padding-left: 0;
+    margin-left: 0;
+    display: inline;
+}
+
+.blogFeedNav li {
+    list-style: none;
+    display: inline;
+    padding-right: 6px;
+}
+
+/* MY CHANGES START HERE */
+/* ---------------------  =Admin list ------------------------ */
+
+.adminlist {
+    width: 200px;
+    margin-bottom: 0;
+}
+
+.adminlist h3 {
+    font-size:12px;
+    font-weight: bold;
+    margin:9px auto 9px auto;
+}
+
+.adminlist p {
+    margin:0 auto;
+    padding-bottom:1em;
+}
+
+.adminlist dl {
+    margin-right:20px;
+}
+
+.adminlist dt {
+    font-size:12px;
+    font-weight:bold;
+    color:#CC0000;
+    margin:12px 0 1px 0;
+    padding:0;
+}
+
+.adminlist dd {
+    margin:0 0 3px 0;
+}
+
+.adminlist dt a {
+    color:#CC0000;
+    text-decoration:none;
+    background-image:none;
+    padding-left:0;
+}
+
+.adminlist dd a {
+    background-image:url(/file-access/default/theme/images/common/ico_linkarrow_blue.gif);
+    background-repeat: no-repeat;
+    background-position: 3px 3px;
+    padding-left: 12px;
+    white-space:nowrap;
+}
+
+.adminlist hr {
+    border-top:1px dashed #d5d5d5;
+    color: #ffffff;
+    border-bottom:0;
+    border-left:0;
+    border-right:0;
+    margin-bottom:6px;
+}
+
+.adminlinks {
+    float:right;
+    margin-right: 20px;
+    padding-top: 24px;
+}
+
+span.error {
+    color: black;
+    background-color: #fef9e6;
+    border: 1px solid red;
+    padding: 5px;
+    display: block;
+}
+
+span.info {
+	background-color:#FEF9E6;
+	border:1px solid #F9BA82;
+	color:#535353;
+	font-size:10px;
+	display:block;
+	padding:12px;
+}
+
+span.required {
+    color: red;
+}
+
+.bold {
+    font-weight: bold;
+}
+
+.empty {
+
+}
+
+.adminforms .selectwide {
+    width: 400px;
+    margin-bottom: 8px;
+}
+
+.messages {
+
+}
+
+/* =Cheyenne's styles begin here 3/12/08  */
+
+
+.globalOps ul{
+    list-style: none;
+}
+
+.globalOps li{
+    margin: -20px 5px 10px 0px;
+    float: right;
+}
+
+.feedTable .basetablestyle th {
+    height:20px;
+    padding-left: 6px;
+    padding-right: 30px;
+}
+
+.feedheader h4{
+    color: #FFFFFF;
+    font-size:14px;
+    margin: 5px 0px 5px 3px;
+    padding:10px 0px 0px 0px;
+}
+
+.feedTable{
+    margin: 25px 0px 15px 0px;
+}
+
+.feedTable .mainHeader2 {
+    float:left;
+}
+
+.feedTable .formbuttons {
+    float:right;
+}
+
+
+.feedOps {
+    background-color:#FFFFFF;
+}
+
+.newFeed {
+    font-size: 10px;
+    font-weight: bold;
+    background-color: #4a5d75;
+    border-top: 1px solid #94aebd;
+    border-left: 1px solid #94aebd;
+    border-right: 1px solid #233345;
+    border-bottom: 1px solid #233345;
+    height:15px;
+    padding:5px 10px 0px 10px;
+    color: #CC3333;
+    float:right;
+    margin: 10px -24px 0px 1px;
+}
+
+.newFeed a{
+    color: #FFFFFF;
+    text-decoration: none;
+}
+
+.newGroup {
+    font-size: 10px;
+    font-weight: bold;
+    color: #FFFFFF;
+    background-color: #4a5d75;
+    border-top: 1px solid #94aebd;
+    border-left: 1px solid #94aebd;
+    border-right: 1px solid #233345;
+    border-bottom: 1px solid #233345;
+    height:15px;
+    padding:5px 10px 0px 10px;
+    float:right;
+    margin: 10px 5px 0px -10px;
+
+}
+
+.newGroup a{
+    color: #FFFFFF;
+    text-decoration: none;
+}
+
+.rowline select {
+    width: 120px;
+}
+
+/*  Archive List css from blog_styles.css  */
+
+.blogRightsidebox {
+    padding: 20px;
+    float:right;
+    width:200px;
+}
+
+.blogRightsidebox h4 {
+    font-size:11px;
+    font-weight:bold;
+    padding-left:10px;
+    padding-bottom: 5px;
+    border-bottom: 1px solid #8c8f91;
+}
+
+.blogRightsidebox ul {
+    padding-left: 10px;
+    margin-left: 0px;
+}
+
+.blogRightsidebox li {
+    list-style: none;
+    display: block;
+    padding:.25em 0px;
+}
+
+/*  Homepage css  */
+
+#columnleftBLOG {
+    float:left;
+    margin: 10px;
+    width:680px;
+}
+
+#columnleftBLOG  h4{
+    color: #cc0000;
+    padding: 0px;
+    margin: 0px;
+}
+
+#columnrightBLOG {
+    float:right;
+    width: 225px;
+}
+
+#columnrightBLOG img{
+    border: none;
+    margin-bottom:10px;
+
+}
+
+.laundrytable {
+    width: 680px;
+    text-align: left;
+    line-height: 150%;
+    margin: 0px 0px 20px 20px;
+}
+
+.laundrytable th {
+    vertical-align: top;
+    padding: 0px 5px 0px 0px;
+    font-weight: bold;
+}
+
+.laundrytable td {
+    vertical-align: top;
+    padding: 0px 5px 5px 0px;
+    color: #000000;
+    border-bottom-color: #e1e1e1;
+    border-bottom-width: 1px;
+    border-bottom-style: solid;
+
+}
+
+/* Homepage: Right column - Blog Subnav */
+
+.TwoColumnBlogSubnav {
+    float: right;
+    border: 1px solid #94aebd;
+    background-color: #e1eef4;
+    background-image:url(/file-access/default/theme/images/common/hdr_feed_gradient.gif);
+    background-repeat: repeat-x;
+    background-position:top;
+    padding:0px 12px 12px 11px;
+    margin:0 0 10px 0;
+    width:200px;
+}
+
+.TwoColumnBlogSubnav h4{
+    margin: 0;
+    padding: 0;
+}
+
+.TwoColumnBlogSubnav dt{
+    font-weight:bold;
+    font-size: 14px;
+    color: white;
+    padding:6px 0px 12px 0px;
+    margin:0px;
+}
+.TwoColumnBlogSubnav dd  {
+    background-image:url(/file-access/default/theme/images/common/ico_linkarrow_blue.gif);
+    color: #233446;
+    background-repeat: no-repeat;
+    background-position: 0px 3px;
+    padding:0 0 6px 15px;
+    margin:0px;
+}
+
+.TwoColumnBlogSubnav ul {
+    margin: 0px;
+    padding:0px;
+}
+
+.TwoColumnBlogSubnav li  {
+    margin: 0;
+    padding: 10px 10px 10px 10px;
+    border-bottom: 1px solid #000000;
+    list-style: none;
+}
+
+.TwoColumnBlogSubnav li.last {
+    border-bottom: none;
+}
+
+
+/* Homepage: Right column - Blog Jelly Box */
+#TwoColumnBlogJelly {
+    float: right;
+    background-repeat:no-repeat;
+    background-position:top;
+    margin-bottom:10px;
+}
+
+
+/* Edit feeds page - additions to Admin List */
+
+.TwoColumnBlogSubnav h4 {
+    font-weight:bold;
+    font-size: 14px;
+    color: white;
+    padding:6px 0px 12px 0px;
+}
+
+hr.formSeparator {
+    margin: 10px 23px 25px auto;
+    border-top: 1px solid #d7d9d9;
+}
+
+.adminforms .checkbox{
+    margin-left:170px;
+}
+
+.submit_first {
+    font-size: 10px;
+    font-weight: bold;
+    color: #FFFFFF;
+    background-color: #e3a835;
+    border-top: 1px solid #fbdea4;
+    border-left: 1px solid #fbdea4;
+    border-right: 1px solid #976c18;
+    border-bottom: 1px solid #976c18;
+    height:20px;
+}
+
+span.instructional_text p {
+	background-color: #f6f6f6;
+	color:#535353;
+	font-size: 10px;
+	padding: 12px;
+	margin: 0px 0 10px 0;
+}
+
+/* Feeds Pages: Right column - Subnav list */
+
+.feedsRightsidebox {
+    padding: 0px 20px 20px 40px;
+    float:right;
+    width:260px;
+    height:100%;
+}
+
+.feedsRightsidebox img{
+    margin:0px 0px 15px 10px;
+    border: 0;
+}
+
+.feedsRightsidebox h4 {
+    font-size:11px;
+    font-weight:bold;
+    padding:3px 0px 3px 10px;
+    margin:10px 10px -10px 0px;
+    border-bottom: 1px solid #8c8f91;
+}
+
+.feedsRightsidebox ul {
+    padding-left: 13px;
+}
+
+.feedsRightsidebox li {
+    list-style:none;
+    padding:.25em 0px;
+}
+
+#feeds_subnav {
+    margin:0px 0 10px 10px;
+    padding:0px;
+}
+
+#feeds_subnav li{
+    background-image:url(/file-access/default/theme/images/common/ico_linkarrow_blue.gif);
+    padding:0px 0 6px 10px;
+    background-repeat: no-repeat;
+    background-position: 0px 3px;
+    font-size: 11px;
+    color: #333366;
+    margin:0px 0px 0px -10px;
+    list-style: none;
+}
+
+.feedsContent {
+/* spacing for when the archive is added    margin: 0px 230px 0px 15px;   */
+    margin: 0px 325px 0px 15px;
+    padding: 0px;
+}
+
+.feedsContent p {
+    margin: .5em auto;
+    padding: 0px;
+}
+
+#feedstblogentry {
+    margin-top: 0px;
+    border-top: 0px;
+}
+
+.feedsContent h3 {
+    margin: auto auto 0 auto;
+    padding: 0;
+    line-height:2em;
+}
+
+.feedsContent hr {
+    border: none 0;
+    border-top: 1px solid #8c8f91;
+    height: 1px;
+    margin-bottom: 1em;
+}
+
+.feedsContent .blogauthortag {
+    margin:0px Auto;
+    padding-bottom: .5em;
+    font-size:10px;
+    font-weight:normal;
+}
+
+.feedsContent .blogcategorytag {
+    margin:2em Auto 1.5em Auto;
+    padding:0px;
+    font-size:10px;
+    font-weight:normal;
+}
+
+.feedsContent .blogcommentsheader {
+    margin: 1.5em auto 0px auto;
+    padding: 0px;
+    line-height:2em;
+    border-top: 1px dotted #a5a5a5;
+    background-color:#FFFFFF;
+    font-size:11px;
+    font-weight:bold;
+}
+
+.feedsContent .blogcommentsbody {
+    margin: 0px auto 1em auto;
+    padding-bottom: 6px;
+    border-bottom: 1px dashed #d5d5d5;
+    background-color:#FFFFFF;
+}
Added: feeds100P26/view/stylesheet/org_layout.css
===================================================================
--- feeds100P26/view/stylesheet/org_layout.css	                        (rev 0)
+++ feeds100P26/view/stylesheet/org_layout.css	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,84 @@
+/********** org layout: Knowledge Base, Contribute, My.ORG  **********/
+
+#ContentTable {
+	width: 960px;
+}
+
+.orgportlet-column {
+	vertical-align: top;
+}
+
+#ORGSubContent {
+	padding-left: 6px;
+	padding-right: 6px;
+	padding-bottom: 6px;
+}
+
+#orgtriple {
+	width: 960px;
+	font-size: 0px;
+	padding-left: 6px;
+	padding-right: 6px;
+	padding-top: 6px;
+}
+
+#orgleft {
+
+}
+
+#orgright {
+
+}
+
+#left1_6 {
+	float:left;
+	padding: 0px 25px 15px 10px;
+	width: 175px;
+}
+
+#center5_6 {
+	float: left;
+	width: 755px;
+}
+
+#orgmaximized {
+	width: 960px;
+}
+
+/* Home maximized */
+
+#orghomemaximized {
+	padding: 10px 15px 10px 10px;
+}
+
+/* Project details */
+
+.orgprojectdetail-innerleft {
+	vertical-align: top;
+	/*width: 260px;*/
+	width: 390px;
+}
+
+/*.orgprojectdetail-innerright {
+	vertical-align: top;
+	width: 260px;
+}*/
+
+.orgprojectdetail-right {
+	vertical-align: top;
+	/*width: 260px;*/
+	width: 390px;
+}
+
+#orgprojectdetailsleft{
+}
+
+#orgprojectdetailscenter {
+}
+
+/* Project details maximized */
+
+#orgprojectdetailsmaximized {
+        padding: 10px 15px 10px 10px;
+}
+

Added: feeds100P26/view/stylesheet/org_main.css
===================================================================
--- feeds100P26/view/stylesheet/org_main.css	                        (rev 0)
+++ feeds100P26/view/stylesheet/org_main.css	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,1060 @@
+body {
+	margin: 0px;
+	padding: 0px;
+	text-align: center;
+	background-color:#e6e7e8;
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/bkgheader_left.gif);
+	background-repeat:repeat-x;
+	font-family: 'Lucida Grande', Geneva, Verdana, Arial, sans-serif;
+	font-size:11px;
+}
+
+#container {
+	margin: 0px auto;
+	width: 969px;
+	text-align: left;
+}
+
+#ORGheader {
+	margin: 0px auto;
+	width: 974px;
+	height:65px;
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/bkgheader_noleft.gif);
+	background-repeat:repeat-x;
+	background-color:#3b4f66;
+}
+
+#contentcontainer {
+	clear:both;
+	margin: 0px auto;
+	background-color:#FFFFFF;
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/bkgblur_right.gif);
+	background-repeat:repeat-y;
+	background-position:right;
+	width: 980px;
+	text-align: left;
+}
+
+#ORGLogo {
+	float:left;
+}
+
+/* --------------------- common elements -------------------------------------------- */
+a {
+	color:#4a5d75;
+}
+
+.clear { clear:both;}
+
+.floatright {
+	float:right;
+}
+.floatleft {
+	float:left;
+}
+
+#majorsectiontitle{
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/hdr_border_gradient.gif);
+	background-repeat:repeat-x;
+	background-position:bottom;
+	color: #CC0000;
+	font-size:16px;
+	margin: 0px 7px 20px 0px;
+	padding:20px 0px 10px 15px;
+}
+
+.nomargin {
+	margin: 0px;
+}
+
+.greyHR {
+	border-top:1px solid #a1a1a1; 
+	background-color:#ffffff;
+	border-bottom:0px;
+	border-left:0px;
+	border-right:0px; 
+}
+.dashedHR {
+   border-top: 1px dashed #d5d5d5;
+   border-bottom: 0px;
+   border-left: 0px;
+   border-right: 0px;
+}
+
+/* ---------------- Multicolumn layout and specs  -----------------------------------  */
+
+
+#ORGContent {
+}
+
+
+/* ----------------    Primary Navigation (Tabbed Nav)  ----------------------------  */
+
+#primarynav {
+	clear:right;
+	float:right;
+	margin-top:14px;
+}
+
+#primarynav ul {
+	margin: 0px;
+	padding:0px;
+}
+
+#primarynav ul li {
+	display: inline;
+	list-style: none;
+	text-align:center;
+	float:left;
+	background-image: url(http://labs.jboss.com/file-access/default/theme/images/common/navtabsbkg_norm.gif);
+	background-repeat: repeat-x;
+	background-position: right top;
+
+}
+
+#primarynav ul a {
+	display: inline;
+	float: left;
+	text-decoration: none;
+	font-size:12px;
+	font-weight:bold;
+	color: white;
+	padding: 5px 20px 3px 20px;
+	background-repeat: no-repeat;
+	background-position: top left;
+	border:1px solid #4a5d75;
+
+}
+
+#primarynav ul a:hover {
+	text-decoration: underline;
+	color: white;
+	background-image: url(http://labs.jboss.com/file-access/default/theme/images/common/navtabsbkg_hover.gif);
+	background-repeat: repeat-x;
+	background-position: top left;
+}
+
+#primarynav #current {
+	font-size: 12px;
+	font-weight: bold;
+}
+
+#primarynav #current a, #primarynav a:hover {
+	background-image:none;
+	background-color: white;
+	color: #636464;
+
+}
+
+/* --------  Utility Navigation  --------------------  */
+
+#utilitynav {
+	padding-top:6px;
+	height:20px;
+}
+
+#utilitynav ul {
+	float: right;
+	margin:0px;
+	padding: 0px;
+	font-size: 10px;
+	color: #8c8f91;
+}
+
+#utilitynav li {
+	display: inline;
+	list-style: none;
+}
+
+#utilitynav li a {
+	font-weight:bold;
+	color: #FFFFFF;
+}
+
+#utilitynav input {
+	padding:0px;
+	vertical-align:middle;
+}
+
+/* ----------------------- subheadnavigaton ----------------- */
+/* this navigation is used inside a page where a "tabbed" approach is necessary. */
+.subheadnavigaton {
+	margin: 0px 20px;
+	border-bottom: 1px solid #a1a1a1;
+	text-align:right;
+}
+
+.subheadnavigaton ul {
+	margin-bottom: 4px;
+}
+
+.subheadnavigaton li {
+	display:inline;
+	color: #656565;
+	font-size:11px;
+	font-weight:bold;
+}
+
+.subheadnavigaton a {
+	color:#4a5d75;
+}
+
+
+/* -------------------- Footer  ----------------------------  */
+.footer {
+	clear:both;
+	text-align:center;
+	color:#a1a1a1;
+	font-size:10px;
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/bkgblur_bottom.gif);
+	background-repeat:no-repeat;
+	background-position:top center;
+	background-color:#e6e7e8;
+	padding:6px 0px;
+
+}
+.footer a {
+	color:#a1a1a1;
+}
+.footer p {
+	margin:0px;
+	padding:1px;
+}
+
+/* ----------------------- Home Page Specific Styles -------------------------  */
+
+
+#columnleftHOME {
+	float: left;
+	padding-left: 12px;
+	width:646px;
+}
+
+#columnrightHOME {
+	float:left;
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/bkg_home_2ndcol.gif);
+	background-repeat:repeat-y;
+	background-position:right;
+}
+
+.knowledgebaseHome {
+	margin:0px;
+	padding:0px;
+	font-size:12px;
+}
+
+.knowledgebaseHome h3 {
+	font-size:16px;
+	font-weight:bold;
+	color:#CC0000;
+	margin:15px 0px 5px 0px;
+	padding:0px;
+}
+.knowledgebaseHome hr {
+	border-top:1px solid #a1a1a1; 
+	background-color:#ffffff;
+	border-bottom:0px;
+	border-left:0px;
+	border-right:0px; 
+}
+
+.homespot { 
+	border: 1px solid #94aebd; 
+	background-color: #e1eef4;
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/portlethdr_home.gif);
+	background-repeat:repeat-x;
+	background-position:top;
+	padding:0px 12px 12px 12px;
+	margin:10px;
+	width:265px; 
+}
+
+.homespot h3 { 
+	font-weight:bold;
+	font-size: 14px;
+	color: white;
+	margin: 0px;
+	padding:6px 0px;
+}
+.homespot h4 { 
+	font-weight:bold;
+	font-size: 12px;
+	margin: 0px;
+	padding-top: 6px;
+}
+
+.homespot hr {
+	border-top:1px dashed #94aebd; 
+	color: #e1eef4;
+	border-bottom:0px;
+	border-left:0px;
+	border-right:0px;
+}
+		
+.homespot p { margin: 3px;}
+
+.homeOrgAnnounce { 
+	border: 1px solid #655050; 
+	background-color: #faf8ed;
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/portlethdr_homeAnnounce.gif);
+	background-repeat: repeat-x;
+	background-position:top;
+	padding:0px 12px 12px 12px; 
+	font-size: 12px;
+	margin:10px;
+	width:265px; 
+}
+
+.homeOrgAnnounce h3 { 
+	font-weight:bold;
+	font-size: 14px;
+	color: white;
+	margin: 0px;
+	padding:6px 0px;
+}
+.homeOrgAnnounce h4 { 
+	font-weight:bold;
+	font-size: 12px;
+	margin: 0px;
+	padding-top: 6px;
+}
+
+.homeOrgAnnounce hr {
+	border-top:1px dashed #655050; 
+	color: #faf8ed;
+	border-bottom:0px;
+	border-left:0px;
+	border-right:0px; 
+}
+		
+.homeOrgAnnounce p { 
+	margin: 3px;
+	padding: 6px 0px 3px 0px;
+}
+
+
+.podcast{ 
+	border:1px solid #646666;
+	margin-top: 12px;
+}	
+.podcastfeed { margin: 0px; padding-right: 0px; padding-top:6px;}
+
+.podcastfeed img { 
+	border:1px solid #94aebd;
+	vertical-align:middle;
+}
+
+/* ---------------------  Projects Main Page Specific Styles : 3 Column layout ------------------------ */
+
+.projectsmainlayout {
+	margin:15px;}
+
+.projectsmainlayout h3 {
+	font-size:12px;
+	font-weight: bold;
+	margin:9px auto 9px auto;
+}
+.projectsmainlayout p {
+	margin:0px auto;
+	padding-bottom:1em;
+	}
+
+.projectsmainlayout dl {
+	margin-right:20px;
+}
+
+.projectsmainlayout dt {
+	font-size:12px;
+	font-weight:bold;
+	color:#CC0000;
+	margin:12px 0px 1px 0px;
+	padding:0px; 
+}
+
+.projectsmainlayout dd { 
+	margin:0px 0px 3px 0px;
+}
+.projectsmainlayout dt a { 
+	color:#CC0000;
+	text-decoration:none;
+	background-image:none;
+	padding-left:0px;
+}
+
+.projectsmainlayout dd a {
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/ico_linkarrow_blue.gif);
+	background-repeat: no-repeat; 
+	background-position: 3px 3px;
+	padding-left: 12px; 
+	white-space:nowrap;
+}
+.projectsmainlayout hr { 
+	margin:0px 0px 3px 0px;
+	border-top:1px dashed #d5d5d5;
+	color: #ffffff;
+	border-bottom:0px;
+	border-left:0px;
+	border-right:0px;
+	margin-bottom:6px;
+}
+
+.projectsmainlayout .projectsmaintextcol {
+	padding-right:30px;
+}
+
+
+.projectsmainlinkcol {
+	width:200px;
+}
+
+/* -----------------------  Base Single Column layout ---------------------------------- */
+
+
+.OneColumnContent {
+	margin: 0px 20px 15px 20px;
+}
+.OneColumnContent h1 {
+	font-size:14px;
+	font-weight: bold;
+}
+.OneColumnContent h2 {
+	font-size:13px;
+	font-weight: bold;
+}
+.OneColumnContent h3 {
+	font-size:12px;
+	font-weight: bold;
+}
+
+/* -----------------------  Project Two Column ---------------------------------- */
+
+
+.TwoColumnContent {
+	padding: 0px 25px 15px 200px;
+}
+
+.TwoColumnContent h1 {
+	font-size:14px;
+	font-weight: bold;
+}
+
+.TwoColumnContent h2 {
+	font-size:13px;
+	font-weight: bold;
+}
+.TwoColumnContent h3 {
+	font-size:12px;
+	font-weight: bold;
+}
+
+
+/* ------------------ Sub Navigation style ---------------------------------------- */
+
+.TwoColumnSubnav {
+	float:left;
+	padding: 0px 0px 15px 10px;
+	/*padding: 0px;*/
+	width: 175px;
+	/*margin: 0px 0px 0px 10px;*/
+}
+
+.TwoColumnSubnav dl {
+	background-color:#b9cae1;
+	margin-top: 0px;
+	margin-bottom:10px;
+}
+
+.TwoColumnSubnav dt {
+	background-color:#3b4e64;
+	padding:5px 0px;
+}
+
+.TwoColumnSubnav dt a { 
+	color:#FFFFFF;
+	font-weight:bold;
+	text-decoration:none;
+	background-image:none;
+	margin-left:10px;
+}
+
+.TwoColumnSubnav dd {
+	margin-left:0px;
+	padding:3px 0px 5px 5px;
+}
+
+.TwoColumnSubnav dd a {
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/ico_linkarrow_blue.gif);
+	color: #233446;
+	background-repeat: no-repeat; 
+	background-position: 3px 3px;
+	padding-left: 12px; 
+}
+
+.TwoColumnSubnav ul {
+	width: 155px;
+	list-style: none;
+	padding-left: 0px;
+	margin-left: 0px;
+	display: block;
+} 
+
+.TwoColumnSubnav ul li {
+	list-style: none;
+	display: block;
+	padding: 5px 10px 2px 10px;
+	text-indent: -12px;
+}
+
+
+/* -------------------  Forms ------------------------------- */
+
+.adminforms {
+	margin:0px 20px 10px 20px;
+}
+
+.adminforms h4 {
+	font-weight:bold;
+	font-size:12px;
+	margin:20px auto 20px auto;
+	border-bottom:1px solid #e6e7e8;
+}
+
+.adminforms label{
+ 	float: left;
+	margin: 0px auto 3px auto;
+	padding-right:10px;
+	white-space:nowrap;
+}
+
+.adminforms input, textarea, select {
+	margin-bottom: 8px;
+}
+
+.adminforms .radios {
+	width: 14px;
+	margin-bottom: -1px;
+}
+.adminforms .selectnarrow {
+	width:80px;
+	margin-bottom: 8px;
+}
+.adminforms .selectmedium {
+	width:160px;
+	margin-bottom: 8px;
+}
+
+.adminforms br {
+	clear: both;
+}
+
+.adminforms hr {
+	border-top:1px dashed #d5d5d5; 
+	border-bottom:0px;
+	color: #ffffff;
+	border-left:0px;
+	border-right:0px;
+}
+
+/* -------------------- Buttons ------------------------------ */
+
+.adminforms .submit {
+	font-size: 10px;
+	font-weight: bold;
+	color: #FFFFFF;
+	background-color: #4a5d75;
+	border-top: 1px solid #94aebd;
+	border-left: 1px solid #94aebd;
+	border-right: 1px solid #233345;
+	border-bottom: 1px solid #233345;
+	height:20px;
+	padding:0px 10px 0px 10px;
+}
+
+.formbuttons {
+	margin: 10px auto 15px auto;
+	padding-top: 5px ;
+	border-top: 1px solid #4a5d75;
+}
+
+.formbuttons ul {
+	padding-left: 0px;
+	margin-left: 0px;
+	display: inline;
+} 
+
+.formbuttons ul li {
+	list-style: none;
+	display: inline;
+	padding-right: 4px;
+}
+
+.formbuttons .submit {
+	font-size: 10px;
+	font-weight: bold;
+	color: #FFFFFF;
+	background-color: #4a5d75;
+	border-top: 1px solid #94aebd;
+	border-left: 1px solid #94aebd;
+	border-right: 1px solid #233345;
+	border-bottom: 1px solid #233345;
+	height:20px;
+}
+
+.singlecolumn .submit {
+ 	width:80px;
+}
+.singlecolumn label {
+	width: 170px;
+}
+
+.singlecolumn input, select {
+	width: 300px;
+}
+.adminforms .contentarrows ul {
+	padding-left: 0px;
+	margin-left: 0px;
+	display: block;
+} 
+
+.adminforms .contentarrows ul li {
+	list-style: none;
+	display: block;	
+	padding: 0px 16px 6px 16px;
+	margin-top: 30px;
+
+}
+.adminforms .contentarrows ul li.last {
+	margin-top: 0px;
+}
+
+#propertiesbox {
+	margin-top: 20px;
+	padding: 0px 10px 10px 10px;
+	background-color: #ececec;
+	border: 1px solid #d5d5d5;
+}
+
+/* -------------------- Info table Style --------------------------------------- */
+
+.basetablestyle, .bodyTable {
+	margin:0px;
+}
+
+.basetablestyle img {
+	border:0px;
+}
+
+.basetablestyle td {
+	height:3em;
+	padding-left: 6px;
+	padding-right: 30px;
+}
+
+.basetablestyle .header {
+	background-color: #233345;
+	font-size:10px;
+	font-weight:bold;
+	color:#94aebd;
+	text-align:left;
+}
+
+.basetablestyle .header a {
+	color:#94aebd;
+}
+
+.basetablestyle .tableheaderfirst {
+	border-bottom:1px solid #233345;
+	height:2em;
+}
+
+.basetablestyle .tableheader {
+	border-left:1px solid #94aebd;
+	border-bottom:1px solid #233345;
+	height:2em;
+}
+
+.basetablestyle .subheader {
+	background-color: #e6e7e8;
+	font-size:10px;
+	font-weight:bold;
+	color:#000000;
+	text-align:left;
+}
+
+.basetablestyle .subheader .tableheaderfirst {
+	height:2em;
+	border-bottom: 0px;
+}
+
+.basetablestyle .subheader .tableheader {
+	height:2em;
+	border-left:1px solid #e6e7e8;
+	border-bottom:0px;
+}
+
+
+
+
+.basetablestyle .rowlinefirst {
+	border-bottom:1px solid #e6e7e8;
+}
+
+.basetablestyle .rowline {
+	border-left:1px solid #e6e7e8;
+	border-bottom:1px solid #e6e7e8;
+}
+
+.basetablestyle .categoryRow {
+    background-color: #dcdedf;
+	font-weight:bold;
+	padding-left: 6px;
+}
+
+.basetablestyle .oddRow, .a {
+    background-color: #f4f3f3;
+	padding-left: 6px;
+}
+
+.basetablestyle .evenRow, .b {
+    background-color: #ffffff;
+}
+
+.basetablestyle .footerrow {
+	background-color:#656565;
+}
+
+.basetablestyle .bottomline {
+	border-bottom:1px solid #656565;
+}
+
+.basetablestyle .topline {
+	border-top:1px solid #656565;
+}
+
+/* ------------------------------------ Project Definitions -------------------------------------- */
+
+.deftable { 
+	width: 100%; 
+	text-align: left; 
+	line-height: 150%;
+	margin-bottom:20px;
+}
+
+.deftable .term { 
+	border-top: 1px dotted #cccccc; 
+	vertical-align: top;
+	padding: 10px;
+	background-color: #f8f9fb; 
+	color: #cc0000; 
+	font-weight: bold;
+}
+
+.deftable .termFirst {
+	border-top: 1px dotted black;
+	vertical-align: top;
+	padding: 10px; 
+	background-color: #f8f9fb;
+	color: #cc0000;
+	font-weight: bold;
+}
+
+.deftable .termLast { 
+	border-top: 1px dotted #cccccc; 
+	border-bottom: 1px dotted black; 
+	vertical-align: top; 
+	padding: 10px; 
+	background-color: #f8f9fb; 
+	color: #cc0000; 
+	font-weight: bold;
+}
+.deftable .def { 
+	border-top: 1px dotted #cccccc; 
+	vertical-align: top; 
+	padding: 10px; 
+	color: #000000;
+}
+
+.deftable .defFirst { 
+	border-top: 1px dotted black; 
+	vertical-align: top; 
+	padding: 10px;
+	color: #000000;
+}
+
+.deftable .defLast { 
+	border-top: 1px dotted #cccccc; 
+	border-bottom: 1px dotted black; 
+	vertical-align: top; 
+	padding: 10px; 
+	color: #000000;
+}
+.deftable .def ul { 
+	margin-left: 1em; 
+	padding-left: 0px; 
+	margin-top: 0px; 
+	margin-bottom: 0px;
+}
+.deftable .def ol { 
+	margin-top: 0px; 
+	margin-bottom: 0px;
+}
+
+.standardLinkArrow {
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/ico_linkarrow_blue.gif);
+	background-repeat: no-repeat; 
+	background-position: 3px 3px;
+	padding: 2px 0px 2px 12px; 
+	white-space:nowrap;
+}
+.standardLinkArrowLeft {
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/ico_linkarrow_left_blue.gif);
+	background-repeat: no-repeat; 
+	background-position: 3px 3px;
+	padding: 2px 0px 2px 12px; 
+	white-space:nowrap;
+}
+.standardFeedLink {
+	background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/ico_12x_feed.gif);
+	background-repeat: no-repeat; 
+	background-position: 0px 3px;
+	padding: 2px 0px 2px 15px; 
+	white-space:nowrap;
+}	
+
+/* ------------  style for tabled blocks with backgrounds (main Resources page) -----  */
+
+.shadedblocktable {
+	background-color:#f4f3f3;
+	border-spacing: 20px;
+	border:1px solid #e6e7e8;
+}
+/* QUICKSTART BOX: From the project pages -----------------------------------------*/
+
+.QuickstartMargin { margin: 0px 0px 30px 30px; float: right; }
+#QuickStart { width: 225px; background-color:#e6e7e8; background-image:url(http://labs.jboss.com/file-access/default/theme/images/common/proj_QuickStart_header.gif); background-repeat:no-repeat; background-position: 15px 10px; border:1px solid #656565; }
+#QuickStart ul { padding: 5px 15px; margin-top: 30px; }
+#QuickStart ul li { border-bottom: 1px solid #656565; list-style: none; font: 11px Verdana, Helvetica, Arial, sans-serif; font-weight:bold; }
+#QuickStart ul li a { padding: 4px 0px 4px 8px; display: block; text-decoration: none; color: #656565;}
+#QuickStart ul li a:hover { background-color: #f4f3f3; color: #000000; }
+#QuickStart img { padding-bottom: 10px; }
+
+/* ----- Feed links  ------ */
+.FeedNav {
+	margin-top: 1em;
+	padding-top: .5em;
+	border-top: 1px solid #8c8f91;
+}
+
+.FeedNav ul {
+	padding-left: 0px;
+	margin-left: 0px;
+	display: inline;
+}
+
+.FeedNav li {
+	list-style: none;
+	display: inline;
+	padding-right: 6px;
+}
+/* --------------------  Login -------------------- */
+
+.logincontainer  {
+	border:1px solid #a1a1a1;
+	padding: 20px 20px 10px 20px;
+	margin: 30px auto 200px auto;
+	width:250px;
+}
+
+.logincontainer p {
+	margin: 3px auto;
+} 
+.logincontainer h4 {
+	font-weight:normal;
+	font-size: 12px;
+	color:#343434;
+	margin: 0px auto 8px auto;
+}
+.failedlogin {
+	font-weight:bold;
+	font-size: 12px;
+	color:#cc0000;
+	padding-top: 20px;
+}
+
+.buttonMed {
+	font-size: 10px;
+	font-weight: bold;
+	color: #FFFFFF;
+	background-color: #4a5d75;
+	border-top: 1px solid #94aebd;
+	border-left: 1px solid #94aebd;
+	border-right: 1px solid #233345;
+	border-bottom: 1px solid #233345;
+	height: 20px;
+}
+/* ---------------------  Code quotation styles ----------------------------- */
+
+.codeQuoteInline {
+	font-family: "Courier New", Courier, monospace;
+	font-size: 10px;
+	padding: auto 6px;
+}
+
+.codeQuoteBlock {
+	background-color:#e6e7e8;
+	font-family: "Courier New", Courier, monospace;
+	font-size:10px;
+	padding:20px;
+	margin: 5px;
+	line-height:150%;
+}
+
+/*  ----------------- Podcast transcript styles  ----------------------------- */
+
+.podcast_transcript {
+  width: 100%;
+  border-collapse: collapse;
+}
+
+.podcast_transcript th {
+  vertical-align: top;
+  padding: 1ex;
+  border-bottom: 1px dotted #ccc;
+}
+
+.podcast_transcript td {
+  vertical-align: top;
+  padding: 1ex;
+  border-bottom: 1px dotted #ccc;
+  line-height: 1.6em;
+}
+.podcast_transcript .First {
+  border-top: 1px dotted #ccc;
+}
+
+.podcast_transcript .Last {
+  border-bottom: 1px dotted #ccc;
+}
+
+.podcast_transcript .interviewer {
+  background-color: #f9f9f9;
+}
+
+#podcastTranscriptContainer h2 {
+	padding-bottom: 3px;
+	padding-top:0px;
+	margin:0px;
+}
+
+#podcastTranscriptContainer h3 {
+	font-size:13px;
+	padding-bottom: 3px;
+	padding-top:0px;
+	margin:0px;
+}
+
+#podcastTranscriptContainer h4 {
+	font-size:11px;
+	font-weight:normal;
+	padding-top:0px;
+	padding-bottom:3px;
+	margin:0px;
+}
+
+
+/* -------------------------- Maven Specific Support ----------------------------------- */
+
+.section {
+  padding: 4px;
+}
+
+.section dl {
+  margin: 12px;
+}
+.section dt {
+  margin:0px;
+  font-weight: bold;
+}
+.section dd {
+	margin: 0px;
+	padding-top:2px;
+	padding-bottom: 8px;
+}
+
+.source {
+  padding: 12px;
+  margin: 1em 7px 1em 7px;
+}
+.source pre {
+  margin: 0px;
+  padding: 10px;
+  overflow:auto;
+  border: 1px solid gray;
+ }
+ 
+.bodyTable {
+	margin: 0px;
+	padding: 0px;
+	border-spacing: 0px;
+}
+
+.bodyTable img {
+	border:0px;
+}
+
+.bodyTable td {
+	height:3em;
+	padding-left: 6px;
+	padding-right: 30px;
+}
+
+.bodyTable th {
+	background-color: #233345;
+	font-size:10px;
+	font-weight:bold;
+	color:#94aebd;
+	text-align:left;
+	padding: 3px;
+}
+
+.bodyTable th a {
+	color:#94aebd;
+}
+
+.bodyTable .a {
+    background-color: #ffffff;
+}
+
+.bodyTable .a td {
+    padding-top: 6px;
+	padding-bottom: 6px;
+	border-bottom:1px solid #e6e7e8;
+}
+
+.bodyTable .b {
+    background-color: #f4f3f3;
+}
+
+.bodyTable .b  td {
+    padding-top: 6px;
+	padding-bottom: 6px;
+	border-bottom:1px solid #e6e7e8;
+}
+
+/* ---------------------------- Warning and Info Styles --------------------------------- */
+
+.messages_info {
+    margin:0 20px 12px -20px;
+    padding:15px;
+    background-color:#fef9e6;
+    border: 1px solid #f9ba82;
+    list-style:none;
+}
+
+.messages_warn {
+    margin:0 20px 12px -20px;
+    padding:15px;
+    background-color: #CC3333;
+    border: 1px solid #7B1E1E; 
+    list-style: none;
+}
\ No newline at end of file

Added: feeds100P26/view/view/feed.xhtml
===================================================================
--- feeds100P26/view/view/feed.xhtml	                        (rev 0)
+++ feeds100P26/view/view/feed.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,51 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="additional_headers">
+        <link rel="alternate" type="application/atom+xml"
+              title="Subscribe to an ATOM feed for '#{feedView.feed.title}'!"
+              href="#{linkService.generateFeedLink(feedView.feed, 'ATOM')}" />
+    </ui:define>
+
+    <ui:define name="header">
+        View feed: #{feedView.feed.title}
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="right_box.xhtml">
+            <ui:param name="feed" value="#{feedView.feed}" />
+            <ui:param name="posts" value="#{feedView.posts}" />
+            <ui:param name="posts_count" value="#{feedView.feed.maxPostsOnPage}" />
+            <ui:param name="more_posts" value="#{feedView.showNext || feedView.showPrevious}" />
+        </ui:include>
+
+        <div class="OneColumnContent">
+            <div class="feedsContent">
+                <ui:include src="feed_toolbar.xhtml">
+                    <ui:param name="additionalStyle" value="blogFeedNav_top" />
+                </ui:include>
+
+                <a:repeat var="post" value="#{feedView.posts}" rows="#{feedView.feed.maxPostsOnPage}">
+                    <a id="#{post.titleAsId}" />
+                    <ui:include src="../common/post.xhtml">
+                        <ui:param name="post" value="#{post}" />
+                        <ui:param name="showSummary" value="false" />
+                        <ui:param name="showAddToHighlights" value="true" />    
+                        <ui:param name="showPostTo" value="true" />
+                    </ui:include>
+                </a:repeat>
+
+                <ui:include src="feed_toolbar.xhtml">
+                    <ui:param name="additionalStyle" value="blogFeedNav_bottom" />
+                </ui:include>
+            </div>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/view/feed_toolbar.xhtml
===================================================================
--- feeds100P26/view/view/feed_toolbar.xhtml	                        (rev 0)
+++ feeds100P26/view/view/feed_toolbar.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,25 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+    <div class="blogFeedNav #{additionalStyle}">
+        <ul>
+            <ui:include src="../common/next_previous_navigation.xhtml">
+                <ui:param name="viewId" value="/view/feed.xhtml" />
+                <ui:param name="navigationBean" value="#{feedView}" />
+                <ui:param name="showColon" value="true" />
+            </ui:include>
+            <li>
+                Subscribe to this feed:
+            </li>
+            <li>
+                <a class="standardFeedLink" href="#{linkService.generateFeedLink(feedView.feed, 'ATOM')}">ATOM</a>
+            </li>
+        </ul>
+    </div>
+</ui:composition>
\ No newline at end of file

Added: feeds100P26/view/view/post.xhtml
===================================================================
--- feeds100P26/view/view/post.xhtml	                        (rev 0)
+++ feeds100P26/view/view/post.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,50 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j"
+                template="../layout/template.xhtml">
+    <ui:define name="header">
+        View post: #{postView.post.title}
+    </ui:define>
+
+    <ui:define name="body">
+        <ui:include src="right_box.xhtml">
+            <ui:param name="feed" value="#{postView.post.feed}" />
+        </ui:include>
+
+        <div class="OneColumnContent">
+            <div class="feedsContent">
+                <div class="blogFeedNav blogFeedNav_top">
+                    <ul>
+                        <li>
+                            &#171; Back to feed: &#160;
+                            <s:link view="/view/feed.xhtml" value="#{postView.post.feed.title}"
+                                    propagation="none">
+                                <f:param name="name" value="#{postView.post.feed.name}"/>
+                            </s:link>
+                        </li>
+                        <s:fragment rendered="#{identity.hasPermission('post', 'delete', post, post.feed, post.feed.group)}">
+                            <li>
+                                <br />
+                                <s:link value="Delete post" action="#{postView.delete}"
+                                        onclick="if (!confirm('Are you sure you want to delete this post?')) return false" />
+                            </li>
+                        </s:fragment>
+                    </ul>
+                </div>
+
+                <ui:include src="../common/post.xhtml">
+                    <ui:param name="post" value="#{post}" />
+                    <ui:param name="showSummary" value="false" />
+                    <ui:param name="showAddToHighlights" value="true" />
+                    <ui:param name="showPostTo" value="true" />
+                </ui:include>
+            </div>
+        </div>
+    </ui:define>
+</ui:composition>

Added: feeds100P26/view/view/right_box.xhtml
===================================================================
--- feeds100P26/view/view/right_box.xhtml	                        (rev 0)
+++ feeds100P26/view/view/right_box.xhtml	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,51 @@
+<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<ui:composition xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:s="http://jboss.com/products/seam/taglib"
+                xmlns:ui="http://java.sun.com/jsf/facelets"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:h="http://java.sun.com/jsf/html"
+                xmlns:rich="http://richfaces.org/rich"
+                xmlns:a="http://richfaces.org/a4j">
+    <div class="feedsRightsidebox">
+        <h4>Navigation</h4>
+        <ul>
+            <li><s:link view="/home.xhtml" value="Feeds home" propagation="none" /></li>
+        </ul>
+
+        <s:div rendered="#{not empty feed.group.header}">
+            <h:outputText value="#{feed.group.header}" escape="false" />
+        </s:div>
+
+        <div id="TwoColumnBlogJelly">
+            <s:link value="" view="/manage/remote/remote_propose.xhtml" propagation="none">
+                <img src="/feeds/images/propose_blog_full.png"
+                     alt="Propose a Blog! If you are blogging on a JBoss-related subject, aggregate it in our system!" />
+            </s:link>
+        </div>
+
+        <s:fragment rendered="#{posts != null}">
+            <h4>Posts of this feed on this page</h4>
+            <ul>
+                <a:repeat var="post" value="#{posts}" rows="#{posts_count}">
+                    <li><a href="##{post.titleAsId}">#{post.title}</a></li>
+                </a:repeat>
+                <s:fragment rendered="#{more_posts}">
+                    <li>(to view more posts, click the next/previous link)</li>
+                </s:fragment>
+            </ul>
+        </s:fragment>
+
+        <h4>Recent posts from all feeds</h4>
+        <ul>
+            <!-- TODO: configure the number of posts -->
+            <ui:repeat var="post" value="#{feedsService.getPosts(0, 10)}">
+                <li>
+                    <s:link view="/view/post.xhtml" value="#{post.title}" propagation="none">
+                        <f:param name="post" value="#{post.titleAsId}"/>
+                    </s:link>
+                </li>
+            </ui:repeat>
+        </ul>
+    </div>
+</ui:composition>
\ No newline at end of file

Added: feeds100P26/view-portlet/feed_not_found.jsp
===================================================================
--- feeds100P26/view-portlet/feed_not_found.jsp	                        (rev 0)
+++ feeds100P26/view-portlet/feed_not_found.jsp	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,11 @@
+<%@ page language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
+<%@ page isELIgnored ="false" %>
+
+<portlet:defineObjects />
+
+<span class="messages_warn">
+The feed: '${requestScope.feedName}' does not exist.
+</span>
\ No newline at end of file

Added: feeds100P26/view-portlet/view.jsp
===================================================================
--- feeds100P26/view-portlet/view.jsp	                        (rev 0)
+++ feeds100P26/view-portlet/view.jsp	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,71 @@
+<%@ page import="org.jboss.blog.service.LinkService" %>
+<%@ page import="org.jboss.blog.model.feed.Feed" %>
+<%@ page import="org.jboss.blog.model.Post" %>
+<%@ page import="org.jboss.blog.tools.StringTools" %>
+<%@ page import="java.text.SimpleDateFormat" %>
+<%@ page language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
+<%@ page isELIgnored ="false" %>
+
+<portlet:defineObjects />
+
+<head>
+    <link href="/feeds/stylesheet/blog.css" rel="stylesheet" type="text/css" />
+</head>
+
+<%
+    Feed feed = (Feed) request.getAttribute("feed");
+    LinkService linkService = (LinkService) request.getAttribute("linkService");
+%>
+
+<div>
+    <c:forEach items="${posts}" var="post">
+        <%
+            Post post = (Post) pageContext.getAttribute("post");
+        %>
+
+        <p>
+            <b>
+                <a href="<%= linkService.generatePostLink(post) %>">${post.title}</a>
+            </b>
+            <br />
+
+            <c:if test="${showDate}">
+                <span class="portlet_author_date">
+                    Posted on <%= SimpleDateFormat.getDateTimeInstance().format(post.getPublished()) %> by
+                    <%= post.getEffectiveAuthor() %>.
+                </span>
+            </c:if>
+
+            <c:if test="${summaryLength > 0}">
+                <br />
+                <br />
+                
+                <span>
+                    <%= StringTools.createSummary(post.getContent(), (Integer) request.getAttribute("summaryLength")) %>
+                </span>
+                
+                <br />
+                <br />
+            </c:if>
+        </p>
+    </c:forEach>
+
+    <div class="blogFeedNav">
+        <ul>
+            <li>
+                <a href="<%= linkService.generateFeedPageLink(feed) %>"
+                   class="standardLinkArrow">
+                    View full feed
+                </a>
+            </li>
+            <li>
+                <a href="/feeds" class="standardLinkArrow">
+                    Go to JBoss.ORG Feeds home
+                </a>
+            </li>
+        </ul>
+    </div>
+</div>
\ No newline at end of file

Added: feeds100P26/view-portlet/view_main.jsp
===================================================================
--- feeds100P26/view-portlet/view_main.jsp	                        (rev 0)
+++ feeds100P26/view-portlet/view_main.jsp	2009-03-26 13:55:24 UTC (rev 350)
@@ -0,0 +1,67 @@
+<%@ page import="org.jboss.blog.service.LinkService" %>
+<%@ page import="org.jboss.blog.model.feed.Feed" %>
+<%@ page import="org.jboss.blog.model.Post" %>
+<%@ page import="org.jboss.blog.tools.StringTools" %>
+<%@ page import="java.text.SimpleDateFormat" %>
+<%@ page language="java" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
+<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
+<%@ page isELIgnored ="false" %>
+
+<portlet:defineObjects />
+
+<head>
+    <link href="/feeds/stylesheet/blog.css" rel="stylesheet" type="text/css" />
+</head>
+
+<%
+    Feed feed = (Feed) request.getAttribute("feed");
+    LinkService linkService = (LinkService) request.getAttribute("linkService");
+%>
+
+<div class="homespot">
+    <h3>Latest Blogs</h3>
+
+    <c:forEach items="${posts}" var="post">
+        <%
+            Post post = (Post) pageContext.getAttribute("post");
+        %>
+
+        <p>
+            <b>
+                <a href="<%= linkService.generatePostLink(post) %>">${post.title}</a>
+            </b>
+            <br />
+
+            <c:if test="${showDate}">
+                <span class="portlet_author_date">
+                    Posted on <%= SimpleDateFormat.getDateTimeInstance().format(post.getPublished()) %> by
+                    <%= post.getEffectiveAuthor() %>.
+                </span>
+            </c:if>
+
+            <c:if test="${summaryLength > 0}">
+                <span>
+                    <%= StringTools.createSummary(post.getContent(), (Integer) request.getAttribute("summaryLength")) %>
+                </span>
+            </c:if>
+        </p>
+    </c:forEach>
+
+    <div class="blogFeedNav">
+        <ul>
+            <li>
+                <a href="<%= linkService.generateFeedPageLink(feed) %>"
+                   class="standardLinkArrow">
+                    View full feed
+                </a>
+            </li>
+            <li>
+                <a href="/feeds" class="standardLinkArrow">
+                    Go to JBoss.ORG Feeds home
+                </a>
+            </li>
+        </ul>
+    </div>
+</div>
\ No newline at end of file




More information about the jboss-cvs-commits mailing list