|
RIFE : Content management framework
This page last changed on May 20, 2009 by taxaudit.
RIFE provides a Content Management Framework that is geared towards the process of storing, loading, validating, formatting, transforming, versioning, retrieving and streaming of various content types. Currently the emphasis has been placed on providing features that are needed when putting content data into a back-end repository and getting the data back out. The actual management of the content once it is stored has not yet been implemented in an elaborate manner, but this will be done in a future version (plus the integration and implementation of JSR-170 JCRQL to search for particular content). These notes will only briefly cover the main features of the CMF, which is quite comprehensive. OverviewThe impatient reader may choose to skip down to the example. There you'll find the logic that is needed to store news items. Each news item has validated XHTML content and an optional image that is automatically scaled into two formats during storage and afterwards streamed directly to the browser. You'll see that very little code is needed to provide these functionalities. All CMF content is stored in repositories that must have unique names. The installation of a content manager creates an initial default repository. If others are needed, they must be created explicitly. repository:path If the repository: prefix is omitted, the content will be stored in the 'default' repository. The path should start with a slash, which makes it 'absolute'; this is completely analogous to unix file system paths. Content is stored through an instance of ContentManager and RIFE includes implementations for the storage in PostgreSQL, MySQL, Oracle, McKoiSQL, HypersonicSQL, Cloudscape and Firebird databases. Below are a few terms and their definitions in the context of the CMF:
WorkflowThe database implementations have the following workflow: Store content
Use content data
ContentQueryManagerTo simplify working with content, a new query manager has been developed, called ContentQueryManager, which extends GenericQueryManager. This query manager class works hand-in-hand with CMF-specific constraints. These additional constraints allow you to provide CMF-related metadata for bean properties while still having access to all regular constraints. The most important additional constraint is 'mimeType'. Setting this constraint directs RIFE to delegate the handling of that property's data to the CMF instead of storing it as a regular column in a database table. The property content location (i.e. its full path) is generated automatically based on the bean class name, the instance's identifier value (i.e. the primary key used by GenericQueryManager), and the property name. So for example, if you have an instance of the NewsItem class whose identifier is 23, then the full path that is generated for a property named text is '/newsitem/23/text'. Note that this always specifies the most recent version of the property, but that older versions are also available from the content store. To make it easy to handle file uploads (which are quite common when uploading content), the ConstrainedProperty class now has an additional file() constraint that will automatically create a file parameter for a submission bean and populate the property with the uploaded data after a submission. Before being able to use the CMF and a ContentQueryManager, you must install both of them, as in this example: Datasource ds = Datasources.getRepInstance().getDatasource("datasource"); DatabaseContentFactory.getInstance(ds).install(); new ContentQueryManager(ds, NewsItem.class).install(); Example: News ItemHere is a short example that demonstrates how to integrate a user-defined compound document called a "news item" with the CMF. A single image associated with the news item will be defined to have two sizes for delivery, "small" and "medium". When it is stored to the CMF, the image is rescaled in accordance with these size constraints, and it can also be automatically converted from other formats (such as PNG). These variants can also be streamed directly to a browser, based on which access path (small or medium) is used. the java bean NewsItem.java:package com.mypackage; import com.uwyn.rife.cmf.*; import com.uwyn.rife.site.*; public class NewsItem extends MetaData { private int mId = -1; private String mTitle = null; private String mText = null; private byte[] mNewsImage = null; protected void activateMetaData() { addConstraint(new ConstrainedProperty("title") .notNull(true) .maxLength(90)); // specify a type for automatic validation addConstraint(new ConstrainedProperty("text") .mimeType(MimeType.APPLICATION_XHTML) .autoRetrieved(true) .fragment(true) .notNull(true) .notEmpty(true)); addConstraint(new ConstrainedProperty("newsImage") .persistent(false) .file(true)); // specify types for content delivery and sizes for automatic rescaling addConstraint(new ConstrainedProperty("imageSmall") .mimeType(MimeType.IMAGE_JPEG) .contentAttribute("width", 70) .editable(false)); addConstraint(new ConstrainedProperty("imageMedium") .mimeType(MimeType.IMAGE_JPEG) .contentAttribute("width", 120) .editable(false)); addConstraint(new ConstrainedProperty("id") .editable(false) .saved(false)); } public void setId(int id) { mId = id; } public int getId() { return mId; } public void setTitle(String title) { mTitle = title; } public String getTitle() { return mTitle; } public void setText(String text) { mText = text; } public String getText() { return mText; } // As always in RIFE, a setter/getter pair defines a property. // In this case, imageSmall and imageMedium are both "virtual" // properties, which reference the mNewsImage property, but // are delivered after transformations, i.e. they are resized // to the dimensions specified in the above Constraints. public void setNewsImage(byte[] newsImage) { mNewsImage = newsImage; } public byte[] getNewsImage() { return mNewsImage; } public void setImageSmall(byte[] imageSmall) { } // dummy setter public byte[] getImageSmall() { return mNewsImage; } public void setImageMedium(byte[] imageMedium) { } // dummy setter public byte[] getImageMedium() { return mNewsImage; } } the element declaration add_news.xml:<element implementation="com.mypackage.elements.AddNews"> <submission name="add"> <bean classname="com.mypackage.NewsItem"/> </submission> </element> the element implementation AddNews.java:package com.mypackage.elements; public class AddNews extends Element { private Template mTemplate; public void initialize() { mTemplate = getHtmlTemplate("add_news"); } public void processElement() { print(mTemplate); } public void doAdd() { NewsItem newsitem = (NewsItem)getSubmissionBean(NewsItem.class); if (!newsitem.validate()) { generateForm(mTemplate, newsitem); print(mTemplate); return; } Datasource ds = Datasources.getRepInstance().getDatasource("datasource"); ContentQueryManager manager = new ContentQueryManager(ds, NewsItem.class); manager.save(newsitem); mTemplate.setBlock("content", "content_success"); print(mTemplate); } } This code takes care of all the logic to store a new news item with two variants of an optional image (small and medium) that are automatically scaled and stored as JPEGs in the CMF together with the article's text. The text will be validated and only valid XHTML Transitional 1.0 will be accepted. Depending upon which image handling libraries are available in the classpath of the application, the image when it is uploaded can be in one of many other formats which can be automatically converted to JPEG before storage into the CMF. Without any external libraries only the JDK standard types are supported: JPEG, GIF and PNG. To display the news item in a browser, some of the work is done automatically for you for the text property since it has the autoRetrieved constraint. This will automatically fetch the content from the ContentStore and fill it into the bean as if it were stored in the actual news item table in a dedicated column. For images, you cannot use this since you have to generate some HTML code which fetches the images themselves from a dedicated URL (the src attribute of the image tag). The CMF provides an element implementation that streams content data directly to a browser according to the path info that the element receives. This path info will be used as the content path when it is retrieved from the ContentManager. You must include both the element and an exit to the element. You can for example integrate this element in your site structure like this: <globalexit name="SERVE_CONTENT" destid="SERVE_CONTENT"/> <element id="SERVE_CONTENT" file="rife/cmf/serve_content.xml" url="/content/*"> <property name="datasource"><datasource><config param="DATASOURCE"/></datasource></property> </element> The 'datasource' property has to contain the name of the datasource to use, which in this case is fetched from the config. Once this element is in place, you just need a template to display the news item, like this for example: <div><!--V 'title'/--></div> <div><!--V 'image'--><!--/V--></div> <div><!--V 'content'/--></div> The following element implementation will display the news item according the id that has to be provided as an input: package com.mypackage.elements; import com.uwyn.rife.cmf.*; import com.uwyn.rife.cmf.dam.*; import com.uwyn.rife.database.*; import com.uwyn.rife.config.*; public class ViewNews extends NewsCommon { public void processElement() { Template t = getHtmlTemplate("view_news"); int id = getInputInt("id"); Datasource ds = Datasources.getRepInstance().getDatasource(Config.getRepInstance().getString("DATASOURCE")); ContentQueryManager manager = new ContentQueryManager(ds, NewsItem.class); NewsItem newsitem = (NewsItem)manager.restore(id); if (null == newsitem) { return; } t.setBean(newsitem); if (manager.hasContent(newsitem, "imageMedium")) { //params for getContentForHtml are: the bean, the content property, an element, and the content exit t.setValue("image", manager.getContentForHtml(newsitem, "imageMedium", this, "SERVE_CONTENT")); } } } |
| Document generated by Confluence on Oct 19, 2010 14:57 |