Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Events
Videos
Audiobooks
Packt Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials

7019 Articles
article-image-obtaining-alfresco-web-content-management-wcm
Packt
22 Oct 2009
11 min read
Save for later

Obtaining Alfresco Web Content Management (WCM)

Packt
22 Oct 2009
11 min read
You must obtain and install an additional download to enable Alfresco WCM functionality. The download includes a new Spring bean configuration file, a standalone Tomcat instance pre-configured with JARs, and server settings that allow a separate Tomcat instance (which is called the virtualization server) to run web applications stored in Alfresco WCM web folders. This capability is used when content managers "preview" an asset or a website. Just as in the core Alfresco server, you can either build the WCM distribution from source or obtain a binary distribution. Step-by-Step: Installing Alfresco WCM If you are building from source, the source code for Alfresco WCM is included with the source code for the rest of the product. Once the source code is checked out, all you have to do is run the distribute Ant task as follows: ant -f continuous.xml distribute After several minutes, the WCM distribution will be placed in the build|dist directory of your source code's root directory. Alternatively, if you are using binaries, download the binary distribution of the Alfresco WCM extension. Where you get it depends on whether you are running Labs or Enterprise. The Labs version is available for download from http://www.alfresco.com. The Enterprise version can be downloaded from the customer or partner site using the credentials provided by your Alfresco representative. Regardless of whether you chose source or binary, you should now have an Alfresco WCM archive. For example, the Labs edition for Linux is named alfresco-labs-wcm-3b.tar.gz. To complete the installation, follow these steps: Expand the archive into any directory that makes sense to you. For example, on my machine I use |usr|local|bin|alfresco-labs-3.0-wcm. Copy the wcm-bootstrap-context.xml file to the Alfresco server's extension directory ($TOMCAT_HOME|shared|classes|alfresco|extension). Edit the startup script (virtual_alf.sh) to ensure that the APPSERVER variable is pointing to the virtual-tomcat directory in the location to which you expanded the archive. Using the example from the previous step, the APPSERVER variable would be: APPSERVER=|usr|local|bin|alfresco-labs-3.0-wcm|virtual-tomcat Start the virtual server by running: |virtual_alf.sh start</i> Start the Alfresco server (or restart it if it was already running). You now have Alfresco with Alfresco WCM up and running. You'll test it out in the next section, but you can do a smoke test by logging in to the web client and confirming that you see the Web Projects folder under Company Home. Creating Web Projects A web project is a collection of assets, settings, and deployment targets that make up a website or a part of a website. Web projects are stored in web project folders, which are regular folders with a bunch of web project metadata. The number of web project folders you use to represent a site, or whether multiple sites are contained within a single web project folder is completely up to you. There is no "right way" that works for everybody. Permissions are one factor. The ability to set permissions stops at the website. Therefore, if you have multiple groups that maintain a site that are concerned with the ability of one to change the other's files, your only remedy is to split the site across web project folders. Web form and workflow sharing is another thing to think about. As you'll soon learn, workflows and web forms are defined globally, and then selectively chosen and configured by each site. Once made available to a web project, they are available to the entire web project. For example, you can't restrict the use of a web form to only a subset of the users of a particular site. SomeCo has chosen the approach of using one web project folder to manage the entire SomeCo.com website. Step-by-Step: Creating the SomeCo Web Project The first thing you need to do is create a new web project folder for the SomeCo website. Initially, you don't need to worry about web forms, deployment targets, or workflows. The goal is simply to create the web project and import the contents of the website. To create the initial SomeCo web project, follow these steps: Log in as admin. Go to Web Projects under Company Home. Click Create, and then Create Web Project. Specify the name of the web project as SomeCo Corporate Site. Specify the DNS name as someco-site. Click Next for the remaining steps, taking all defaults. You'll come back later and configure some of these settings. On the summary page, click Finish. You now have a web project folder for the SomeCo corporate site. Click SomeCo Corporate Site. You should see one Staging Sandbox and one User Sandbox. Click the Browse Website button for the User Sandbox. Now you can import SomeCo's existing website into the web project folder. Click Create, and then Bulk Import. Navigate to the "web-site" project in your Eclipse workspace. Assuming you've already run Ant for this project, there should be a ZIP file in the build folder called someco-web-site.zip. Select the file. Alfresco will import the ZIP into your User Sandbox. What Just Happened You just created a new web project folder for SomeCo's corporate website. But upon creation of a web project folder, there is no website to manage. This is a big disappointment for some people. The most crestfallen are those who didn't realize that Alfresco is a "decoupled" content management system—it has no frontend framework and no "default" website like "coupled" content management systems such as Drupal. This will change in the 3.0 releases as Alfresco introduces its new set of clients. But for now, it's up to you to give Alfresco a website to manage. You just happened to have a start on the SomeCo website sitting in your Eclipse workspace. Alfresco knows how to import WAR and ZIP files, which is a convenient way to migrate the website into Alfresco for the first time. Because web project sandboxes are mountable via CIFS, simply copying the website into the sandbox via CIFS is another way to go. The difference between the two approaches is that the WAR/ZIP import can only happen once. The import action complains if an archive contains nodes that already exist in the repository. If you haven't already done so, take a look at the contents of your sandbox. You should see index.html in the root of your User Sandbox and a someco folder that contains additional folders for CSS, images, JavaScript, and so on. The HTML file in the root is the same index.html file you deployed to the Alfresco web application in order to implement the AJAX ratings widget. Click the preview icon. (Am I the only one who thinks it looks eerily similar to the Turkish nazar talisman used to ward off the "evil eye"?) You should see the index page in a new tab or window. The list of Whitepapers won't be displayed. That's because the page is running in the context of the virtualization server, which is a different domain than your Alfresco server. Therefore, it is subject to the cross-domain restriction, which will be addressed later. Playing Nicely in the Sandbox Go back to the root of your web project folder. The link in the breadcrumb trail is likely to be the fastest way to navigate back. Click the Browse Website link in the Staging Sandbox. It's empty. If you were to invite another user to this website, his/her sandbox would be empty as well. Sandboxes are used to isolate changes each content owner makes, while still providing him/her the full context of the website. The Staging Sandbox represents your live website. Or in source code control terms, it is the HEAD of your site. It is assumed that whatever is in the Staging Sandbox can be safely deployed to the live website at any time. It is currently empty because you have not yet submitted any content to staging. Let's go ahead and do that now. If you click the Modified Items link in the User Sandbox, you'll see the index.html file and the someco folder. You could submit these individually. But you want everything to go to staging, so click Submit All: Provide a label and a description such as initial population and click OK. It is safe to ignore the warning that a suitable workflow was not found. That's expected because you haven't configured a workflow for this web project yet. Now the files have been submitted to staging. Here are some things to notice: If you click the Preview Website link in the Staging Sandbox, you'll see the website just as you did in the User Sandbox earlier. If you browse the website in the Staging Sandbox, you'll see the same files currently shown when you browse the website in your User Sandbox. A snapshot of the site was automatically taken when the files were committed and is listed under Recent Snapshots: Inviting Users To get a feel for how sandboxes work, invite one or more users to the web project (Actions, Invite Web Project Users). The following table describes the out of the box web project roles:   WCM User Role Can do these things Content Contributor Create and submit new content; but cannot edit or delete existing content Content Reviewer Create, edit, and submit new content; but cannot delete existing content Content Collaborator See all sandboxes, but only have full control over their own Create, edit, and submit new content; but cannot delete existing content Edit web project settings Content Manager See and modify content in all sandboxes; exert full control over all content See and deploy snapshots and manage deployment reports Edit web project settings Invite new users to the web project Delete the web project and individual sandboxes You'll notice that each new user gets his/her own sandbox, and that the sandbox automatically contains everything that is currently in staging. If a user makes a change to his/her sandbox, it is only visible within their sandbox until they commit the change to staging. If this is done, everyone else sees the change immediately. Unlike some content management and source code control systems, there is no need for other users to do an "update" or a "get latest" to copy the latest changes from staging into their sandbox. It is important to note that Alfresco will not merge conflicts. When a user makes a change to a file in his/her sandbox, it will be locked in all other sandboxes to prevent conflicts. If you were to customize Alfresco to disable locking, the last change would win. Alfresco would not warn you of the conflict. The Alfresco admin user and any user with Content Manager Access can see (and work within) all User Sandboxes. Everyone else sees only their own sandboxes. Mounting Sandboxes via CIFS All sandboxes are individually mountable via CIFS. In fact, in staging, each snapshot is individually mountable. This gives content owners the flexibility to continue managing content in their sandbox using the tools they are familiar with. The procedure for mounting a sandbox is identical to that of mounting the regular repository via CIFS, except that you use "AVM" as the mount point instead of "Alfresco". One difference between mounting the AVM repository through CIFS and mounting the DM repository is that the AVM repository directory structure is more complicated. For example, the path to the root of admin's sandbox in the SomeCo site is: |someco-site--admin|HEAD|DATA|www|avm_webapps|ROOT The first part of the path, someco-site, is the DNS name you assigned when you set up the web project. The admin string indicates which User Sandbox we are looking at. If you wanted to mount to the Staging Sandbox, the first part of the path would be someco-site without --admin. The next part of the path, HEAD, specifies the latest-and-greatest version of the website. Alternatively, you could mount a specific snapshot like this: |someco-site--admin|VERSION|v2|DATA|www|avm_webapps|ROOT As you might expect, the normal permissions apply. Users who aren't able to see another user's sandbox in the web client won't be able to do so through CIFS.
Read more
  • 0
  • 0
  • 2307

Packt
22 Oct 2009
8 min read
Save for later

Working with Rails – ActiveRecord, Migrations, Models, Scaffolding, and Database Completion

Packt
22 Oct 2009
8 min read
ActiveRecord, Migrations, and Models ActiveRecord is the ORM layer (see the section Connecting Rails to a Database in the previous article) used in Rails. It is used by controllers as a proxy to the database tables. What's really great about this is that it protects you against having to code SQL. Writing SQL is one of the least desirable aspects of developing with other web-centric languages (like PHP): having to manually build SQL statements, remembering to correctly escape quotes, and creating labyrinthine join statements to pull data from multiple tables. ActiveRecord does away with all of that (most of the time), instead presenting database tables through classes (a class which wraps around a database table is called a model) and instances of those classes (model instances). The best way to illustrate the beauty of ActiveRecord is to start using it. Model == Table The base concept in ActiveRecord is the model. Each model class is stored in the app/models directory inside your application, in its own file. So, if you have a model called Person, the file holding that model is in app/models/person.rb, and the class for that model, defined in that file, is called Person. Each model will usually correspond to a table in the database. The name of the database table is, by convention, the pluralized (in the English language), lower-case form of the model's class name. In the case of our Intranet application, the models are organized as follows: Table Model class File containing class definition (in app/models) people Person person.rb companies Company company.rb addresses Address address.rb We haven't built any of these yet, but we will shortly. Which Comes First: The Model or The Table? To get going with our application, we need to generate the tables to store data into, as shown in the previous section. It used to be at this point where we would reach for a MySQL client, and create the database tables using a SQL script. (This is typically how you would code a database for a PHP application.) However, things have moved on in the Rails world. The Rails developers came up with a pretty good (not perfect, but pretty good) mechanism for generating databases without the need for SQL: it's called migrations, and is a part of ActiveRecord. Migrations enable a developer to generate a database structure using a series of Ruby script files (each of which is an individual migration) to define database operations. The "operations" part of that last sentence is important: migrations are not just for creating tables, but also for dropping tables, altering them, and even adding data to them. It is this multi-faceted aspect of migrations which makes them useful, as they can effectively be used to version a database (in much the same way as Subversion can be used to version code). A team of developers can use migrations to keep their databases in sync: when a change to the database is made by one of the team and coded into a migration, the other developers can apply the same migration to their database, so they are all working with a consistent structure. When you run a migration, the Ruby script is converted into the SQL code appropriate to your database server and executed over the database connection. However, migrations don't work with every database adapter in Rails: check the Database Support section of the ActiveRecord::Migration documentation to find out whether your adapter is supported. At the time of writing, MySQL, PostgreSQL, SQLite, SQL Server, Sybase, and Oracle were all supported by migrations. Another way to check whether your database supports migrations is to run the following command in the console (the output shown below is the result of running this using the MySQL adapter): >> ActiveRecord::Base.connection.supports_migrations? => true We're going to use migrations to develop our database, so we'll be building the model first. The actual database table will be generated from a migration attached to the model. Building a Model with Migrations In this section, we're going to develop a series of migrations to recreate the database structure outlined in Chapter 2 of the book Ruby on Rails Enterprise Application Development: Plan, Program, Extend. First, we'll work on a model and migration for the people table. Rails has a generate script for generating a model and its migration. (This script is in the script directory, along with the other Rails built-in scripts.) The script builds the model, a base migration for the table, plus scripts for testing the model. Run it like this: $ ruby script/generate model Person exists app/models/  exists test/unit/    exists test/fixtures/    create app/models/person.rb    create test/unit/person_test.rb    create test/fixtures/people.yml    exists db/migrate    create db/migrate/001_create_people.rb Note that we passed the singular, uppercase version of the table name ("people" becomes "Person") to the generate script. This generates a Person model in the file app/models/person.rb; and a corresponding migration for a people table (db/ migrate/001_create_people.rb). As you can see, the script enforces the naming conventions, which connects the table to the model. The migration name is important, as it contains sequencing information: the "001" part of the name indicates that running this migration will bring the database schema up to version 1; subsequent migrations will be numbered "002...", "003..." etc., each specifying the actions required to bring the database schema up to that version from the previous one. The next step is to edit the migration so that it will create the people table structure. At this point, we can return to Eclipse to do our editing. (Remember that you need to refresh the file list in Eclipse to see the files you just generated). Once, you have started Eclipse, open the file db/migrate/001_create_people.rb. It should look like this:     class CreatePeople < ActiveRecord::Migration        def self.up            create_table :people do |t|                # t.column :name, :string            end        end        def self.down            drop_table :people        end    end This is a migration class with two class methods, self.up and self.down. The self.up method is applied when migrating up one database version number: in this case, from version 0 to version 1. The self.down method is applied when moving down a version number (from version 1 to 0). You can leave self.down as it is, as it simply drops the database table. This migration's self.up method is going to add our new table using the create_table method, so this is the method we're going to edit in the next section. Ruby syntaxExplaining the full Ruby syntax is outside the scope of this book. For our purposes, it suffices to understand the most unusual parts. For example, in the create_table method call shown above:,     create_table :people do |t|        t.column :title, :string        ...    end The first unusual part of this is the block construct, a powerful technique for creating nameless functions. In the example code above, the block is initialized by the do keyword; this is followed by a list of parameters to the block (in this case, just t); and closed by the end keyword. The statements in-between the do and end keywords are run within the context of the block. Blocks are similar to lambda functions in Lisp or Python, providing a mechanism for passing a function as an argument to another function. In the case of the example, the method call create_table:people is passed to a block, which accepts a single argument, t; t has methods called on it within the body of the block. When create_table is called, the resulting table object is "yielded" to the block; effectively, the object is passed into the block as the argument t, and has its column method called multiple times. One other oddity is the symbol: that's what the words prefixed with a colon are. A symbol is the name of a variable. However, in much of Rails, it is used in contexts where it is functionally equivalent to a string, to make the code look more elegant. In fact, in migrations, strings can be used interchangeably with symbols.  
Read more
  • 0
  • 0
  • 5395

article-image-access-control-php5-cms-part-2
Packt
21 Oct 2009
17 min read
Save for later

Access Control in PHP5 CMS - Part 2

Packt
21 Oct 2009
17 min read
Framework Solution The implementation of access control falls into three classes. One is the class that is asked questions about who can do what. Closely associated with this is another class that caches general information applicable to all users. It is made a separate class to aid implementation of the split of cache between general and user specific. The third class handles administration operations. Before looking at the classes, though, let's figure out the database design. Database for RBAC All that is required to implement basic RBAC is two tables. A third table is required to extend to a hierarchical model. An optional extra table can be implemented to hold role descriptions. Thinking back to the design considerations, the first need is for a way to record the operations that can be done on the subjects, that is the permissions. They are the targets for our access control system. You'll recall that a permission consists of an action and a subject, where a subject is defined by a type, and an identifier. For ease of handling, a simple auto-increment ID number is added. But we also need a couple of other things. To make our RBAC system general, it is important to be able to control not only the actual permissions, but also who can grant those permissions, and whether they can grant that right to others. So an extra control field is added with one bit for each of those three possibilities. It therefore becomes possible to grant the right to access something with or without the ability to pass on that right. The other extra data item that is useful is a "system" flag. It is used to make some permissions incapable of deletion. Although not being a logical requirement, this is certainly a practical requirement. We want to give administrators a lot of power over the configuration of access rights, but at the same time, we want to avoid any catastrophes. The sort of thing that would be highly undesirable would be for the top level administrator to remove all of their own rights to the system. In practice, most systems will have a critical central structure of rights, which should not be altered even by the highest administrator. So now the permissions table can be seen to be as shown in the following screenshot: Note that the character strings for role, action, and subject_type are given generous lengths of 60, which should be more than adequate. The subject ID will often be quite short, but to avoid constraining generality, it is made a text field, so that the RBAC system can still handle very complex identifiers, if required. Of course, there will be some performance penalties if this field is very long, but it is better to have a design trade-off than a limitation. If we restricted the subject ID to being a number, then more complex identifiers would be a special case. This would destroy the generality of our scheme, and might ultimately reduce overall efficiency. In addition to the auto-increment primary key ID, two indices are created, as shown in the following screenshot. They involve overhead during update operations but are likely to speed access operations. Since far more accesses will typically be made than updates, this makes sense. If for some reason an index does not give a benefit, it is always possible to drop it. Note that the index on the subject ID has to be constrained in length to avoid breaking limits on key size. The value chosen is a compromise between efficiency through short keys, and efficiency through the use of fine grained keys. In a heavily used system, it would be worth reviewing the chosen figure carefully, and perhaps modifying it in the light of studies into actual data. The other main database table is even simpler, and holds information about assignment of accessors to roles. Again, an auto-increment ID is added for convenience. Apart from the ID, the only fields required are the role, the accessor type, and the accessor ID. This time a single index, additional to the primary key, is sufficient. The assignment table is shown in the following screenshot, and its index is shown in the screenshot after that: Adding hierarchy to RBAC requires only a very simple table, where each row contains two fields: a role, and an implied role. Both fields constitute the primary key, neither field on its own being necessarily unique. An index is not required for efficiency, since the volume of hierarchy information is assumed to be small, and whenever it is needed, the whole table is read. But it is still a good principle to have a primary key, and it also guarantees that there will not be redundant entries. For the example given earlier, a typical entry might have consultant as the role, and doctor as the implied role. At present, Aliro implements hierarchy only for backwards compatibility, but it is a relatively easy development to make hierarchical relationships generally available. Optionally, an extra table can be used to hold a description of the roles in use. This has no functional purpose, and is simply an option to aid administrators of the system. The table should have the role as its primary key. As it does not affect the functionality of the RBAC at all, no further detail is given here. With the database design settled, let's look at the classes. The simplest is the administration class, so we'll start there. Administering RBAC The administration of the system could be done by writing directly to the database, since that is what most of the operations involve. There are strong reasons not to do so. Although the operations are simple, it is vital that they be handled correctly. It is generally a poor principle to allow access to the mechanisms of a system rather than providing an interface through class methods. The latter approach ideally allows the creation of a robust interface that changes relatively infrequently, while details of implementation can be modified without affecting the rest of the system. The administration class is kept separate from the classes handling questions about access because for most CMS requests, administration will not be needed, and the administration class will not load at all. As a central service, the class is implemented as a standard singleton, but it is not cached because information generally needs to be written immediately to the database. In fact, the administration class frequently requests the authorization cache class to clear its cache so that the changes in the database can be effective immediately. The class starts off: class aliroAuthorisationAdmin { private static $instance = __CLASS__; private $handler = null; private $authoriser = null; private $database = null; private function __construct() { $this->handler =& aliroAuthoriserCache::getInstance(); $this->authoriser =& aliroAuthoriser::getInstance(); $this->database = aliroCoreDatabase::getInstance(); } private function __clone() { // Enforce singleton } public static function getInstance() { return is_object(self::$instance) ? self::$instance : (self::$instance = new self::$instance()); } private function doSQL($sql, $clear=false) { $this->database->doSQL($sql); if ($clear) $this->clearCache(); } private function clearCache() { $this->handler->clearCache(); } Apart from the instance property that is used to implement the singleton pattern, the other private properties are related objects that are acquired in the constructor to help other methods. Getting an instance operates in the usual fashion for a singleton, with the private constructor, and clone methods enforcing access solely via getInstance. The doSQL method also simplifies other methods by combining a call to the database with an optional clearing of cache through the class's clearCache method. Clearly the latter is simple enough that it could be eliminated. But it is better to have the method in place so that if changes were made to the implementation such that different actions were needed when any relevant cache is to be cleared, the changes would be isolated to the clearCache method. Next we have a couple of useful methods that simply refer to one of the other RBAC classes: public function getAllRoles($addSpecial=false) { return $this->authoriser->getAllRoles($addSpecial); }public function getTranslatedRole($role) { return $this->authoriser->getTranslatedRole($role); } Again, these are provided so as to simplify the future evolution of the code so that implementation details are concentrated in easily identified locations. The general idea of getAllRoles is obvious from the name, and the parameter determines whether the special roles such as visitor, registered, and nobody will be included. Since those roles are built into the system in English, it would be useful to be able to get local translations for them. So the method getTranslatedRole will return a translation for any of the special roles; for other roles it will return the parameter unchanged, since roles are created dynamically as text strings, and will therefore normally be in a local language from the outset. Now we are ready to look at the first meaty method: public function permittedRoles ($action, $subject_type, $subject_id) { $nonspecific = true; foreach ($this->permissionHolders ($subject_type, $subject_id) as $possible) { if ('*' == $possible->action OR $action == $possible->action) { $result[$possible->role] = $this->getTranslatedRole ($possible->role); if ('*' != $possible->subject_type AND '*' != $possible_subject_id) $nonspecific = false; } } if (!isset($result)) { if ($nonspecific) $result = array('Visitor' => $this->getTranslatedRole('Visitor')); else return array(); } return $result; }private function &permissionHolders ($subject_type, $subject_id) { $sql = "SELECT DISTINCT role, action, control, subject_type, subject_id FROM #__permissions"; if ($subject_type != '*') $where[] = "(subject_type='$subject_type' OR subject_type='*')"; if ($subject_id != '*') $where[] = "(subject_id='$subject_id' OR subject_id='*')"; if (isset($where)) $sql .= " WHERE ".implode(' AND ', $where); return $this->database->doSQLget($sql); } Any code that is providing an RBAC administration function for some part of the CMS is likely to want to know what roles already have a particular permission so as to show this to the administrator in preparation for any changes. The private method permissionHolders uses the parameters to create a SQL statement that will obtain the minimum relevant permission entries. This is complicated by the fact that in most contexts, asterisk can be used as a wild card. The public method permittedRoles uses the private method to obtain relevant database rows from the permissions table. These are checked against the action parameter to see which of them are relevant. If there are no results, or if none of the results refer specifically to the subject, without the use of wild cards, then it is assumed that all visitors can access the subject, so the special role of visitor is added to the results. When actual permission is to be granted we need the following methods: public function permit ($role, $control, $action, $subject_type, $subject_id) { $sql = $this->permitSQL($role, $control, $action, $subject_type, $subject_id); $this->doSQL($sql, true); }private function permitSQL ($role, $control, $action, $subject_type, $subject_id) { $this->database->setQuery("SELECT id FROM #__permissions WHERE role='$role' AND action='$action' AND subject_type='$subject_type' AND subject_id='$subject_id'"); $id = $this->database->loadResult(); if ($id) return "UPDATE #__permissions SET control=$control WHERE id=$id"; else return "INSERT INTO #__permissions (role, control, action, subject_type, subject_id) VALUES ('$role', '$control', '$action', '$subject_type', '$subject_id')"; } The public method permit grants permission to a role. The control bits are set in the parameter $control. The action is part of permission, and the subject of the action is identified by the subject type and identity parameters. Most of the work is done by the private method that generates the SQL; it is kept separate so that it can be used by other methods. Once the SQL is obtained, it can be passed to the database, and since it will normally result in changes, the option to clear the cache is set.   The SQL generated depends on whether there is already a permission with the same parameters, in which case only the control bits are updated. Otherwise an insertion occurs. The reason for having to do a SELECT first, and then decide on INSERT or UPDATE is that the index on the relevant fields is not guaranteed to be unique, and also because the subject ID is allowed to be much longer than can be included within an index. It is therefore not possible to use ON DUPLICATE KEY UPDATE. Wherever possible, it aids efficiency to use the MySQL option for ON DUPLICATE KEY UPDATE. This is added to the end of an INSERT statement, and if the INSERT fails by virtue of the key already existing in the table, then the alternative actions that follow ON DUPLICATE KEY UPDATE are carried out. They consist of one or more assignments, separated by commas, just as in an UPDATE statement. No WHERE is permitted since the condition for the assignments is already determined by the duplicate key situation. A simple method allows deletion of all permissions for a particular action and subject: public function dropPermissions ($action, $subject_type, $subject_id) { $sql = "DELETE FROM #__permissions WHERE action='$action' AND subject_type='$subject_type'AND subject_id='$subject_id' AND system=0"; $this->doSQL($sql, true); } The final set of methods relates to assigning accessors to roles. Two of them reflect the obvious need to be able to remove all roles from an accessor (possibly preparatory to assigning new roles) and the granting of a role to an accessor. Where the need is to assign a whole set of roles, it is better to have a method especially for the purpose. Partly this is convenient, but it also provides an extra operation, minimization of the set of roles. The method is: public function assign ($role, $access_type, $access_id, $clear=true) { if ($this->handler->barredRole($role)) return false; $this->database->setQuery("SELECT id FROM #__assignments WHERE role='$role' AND access_type='$access_type' AND access_id='$access_id'"); if ($this->database->loadResult()) return true; $sql = "INSERT INTO #__assignments (role, access_type, access_id) VALUES ('$role', '$access_type', '$access_id')"; $this->doSQL($sql, $clear); return true; }public function assignRoleSet ($roleset, $access_type, $access_id) { $this->dropAccess ($access_type, $access_id); $roleset = $this->authoriser->minimizeRoleSet($roleset); foreach ($roleset as $role) $this->assign ($role, $access_type, $access_id, false); $this->clearCache(); }public function dropAccess ($access_type, $access_id) { $sql = "DELETE FROM #__assignments WHERE access_type='$access_type' AND access_id='$access_id'"; $this->doSQL($sql, true); } The method assign links a role to an accessor. It checks for barred roles first, these are simply the special roles discussed earlier, which cannot be allocated to any accessor. As with the permitSQL method, it is not possible to use ON DUPLICATE KEY UPDATE because the full length of the accessor ID is not part of an index, so again the existence of an assignment is checked first. If the role assignment is already in the database, there is nothing to do. Otherwise a row is inserted, and the cache is cleared. Getting rid of all role assignments for an accessor is a simple database deletion, and is implemented in the dropAccess method. The higher level method assignRoleSet uses dropAccess to clear out any existing assignments. The call to the authorizer object to minimize the role set reflects the implementation of a hierarchical model. Once there is a hierarchy, it is possible for one role to imply another as consultant implied doctor in our earlier example. This means that a role set may contain redundancy. For example, someone who has been allocated the role of consultant does not need to be allocated the role of doctor. The minimizeRoleSet method weeds out any roles that are superfluous. Once that has been done, each role is dealt with using the assign method, with the clearing of the cache saved until the very end. The General RBAC Cache As outlined earlier, the information needed to deal with RBAC questions is cached in two ways. The file system cache is handled by the aliroAuthoriserCache singleton class, which inherits from the cachedSingleton class. This means that the data of the singleton object will be automatically stored in the file system whenever possible, with the usual provisions for timing out an old cache, or clearing the cache when an update has occurred. It is highly desirable to cache the data both to avoid database operations and to avoid repeating the processing needed in the constructor. So the intention is that the constructor method will run only infrequently. It contains this code: protected function __construct() { // Making private enforces singleton $database = aliroCoreDatabase::getInstance(); $database->setQuery("SELECT role, implied FROM #__role_link UNION SELECT DISTINCT role, role AS implied FROM #__assignments UNION SELECT DISTINCT role,role AS implied FROM #__permissions"); $links = $database->loadObjectList(); if ($links) foreach ($links as $link) { $this->all_roles[$link->role] = $link->role; $this->linked_roles[$link->role][$link->implied] = 1; foreach ($this->linked_roles as $role=>$impliedarray) { foreach ($impliedarray as $implied=>$marker) { if ($implied == $link->role OR $implied == $link->implied) { $this->linked_roles[$role][$link->implied] = 1; if (isset($this->linked_roles[$link->implied])) foreach ($this->linked_roles[$link->implied] as $more=>$marker) { $this->linked_roles[$role][$more] = 1; } } } } } $database->setQuery("SELECT role, access_id FROM #__assignments WHERE access_type = 'aUser' AND (access_id = '*' OR access_id = '0')"); $user_roles = $database->loadObjectList(); if ($user_roles) foreach ($user_roles as $role) $this- >user_roles[$role->access_id][$role->role] = 1; if (!isset($this->user_roles['0'])) $this->user_roles['0'] = array(); if (isset($this->user_roles['*'])) $this->user_roles['0'] = array_merge($this->user_roles['0'], $this->user_roles['*']); } All possible roles are derived by a UNION of selections from the permissions, assignments, and linked roles database tables. The union operation has overheads, so that alone is one reason for favoring the use of a cache. The processing of linked roles is also complex, and therefore worth running as infrequently as possible. Rather than working through the code in detail, it is more useful to describe what it is doing. The concept is much simpler than the detail! If we take an example from the backwards compatibility features of Aliro, there is a role hierarchy that includes the role Publisher, which implies membership of the role Editor. The role Editor also implies membership of the role Author. In the general case, it is unreasonable to expect the administrator to figure out the implied relationships. In this case, it is clear that the role Publisher must also imply membership of the role Editor. But these linked relationships can plainly become quite complex. The code in the constructor therefore assumes that only the least number of connections have been entered into the database, and it figures out all the implications. The other operation where the code is less than transparent is the setting of the user_roles property. The Aliro RBAC system permits the use of wild cards for specification of identities within accessor, or subject types. An asterisk indicates any identity. For accessors whose accessor type is user, another wild card available is zero. This means any user who is logged in, and is not an unregistered visitor. Given the relatively small number of role assignments of this kind, it saves a good deal of processing if all of them are cached. Hence the user_roles processing is done in the constructor. Other methods in the cache class are simple enough to be mentioned rather than given in detail. They include the actual implementation of the getTranslatedRole method, which provides local translations for the special roles. Other actual implementations are getAllRoles with the option to include the special roles, getTranslatedRole, which translates a role if it turns out to be one of the special ones and barredRole, which in turn, tests to see if the passed role is in the special group. It may therefore not be assigned to an accessor.
Read more
  • 0
  • 0
  • 2174

article-image-access-control-php5-cms-part-1
Packt
21 Oct 2009
12 min read
Save for later

Access Control in PHP5 CMS - Part 1

Packt
21 Oct 2009
12 min read
The Problem We need to design and implement a role-based access control (RBAC) system, demonstrate its use, and ensure that the system can provide: a simple data structure a flexible code to provide a usable RBAC interface efficiency so that RBAC avoids heavy overheads Discussion and Considerations Computer systems have long needed controls on access. Early software commonly fell into the category that became known as access control lists (ACL). But these were typically applied at a fairly low level in systems, and referred to basic computer operations. Further development brought software designed to tackle more general issues, such as control of confidential documents. Much work was done on discretionary access control (DAC), and mandatory access control (MAC). A good deal of academic research has been devoted to the whole question of access controls. The culmination of this work is that the model most widely favored is the role-based access control system, such a mouthful that the acronym RBAC is used hereafter. Now although the academic analysis can be abstruse, we need a practical solution to the problem of managing access to services on a website. Fortunately, rather like the relational database, the concepts of RBAC are simple enough. RBAC involves some basic entities. Unfortunately, terminologies are not always consistent, so let us keep close to the mainstream, and define some that will be used to implement our solution: Subject: A subject is something that is controlled. It could be a whole web page, but might well be something much more specific such as a folder in a file repository system. This example points to the fact that a subject can often be split into two elements, a type, and an identifier. So the folders of a file repository count as a type of subject, and each individual folder has some kind of identifier. Action: An action arises because we typically need to do more than simply allow or deny access to RBAC subjects. In our example, we may place different restrictions on uploading files to a folder and downloading files from the folder. So our actions might therefore include 'upload', and 'download'. Accessor: The simplest example of an accessor is a user. The accessor is someone or something who wants to perform an action. It is unduly restrictive to assume that accessors are always users. We might want to consider other computer systems as accessors, or an accessor might be a particular piece of software. Accessors are like subjects in splitting into two parts. The first part is the kind of accessor, with website users being the most common kind. The second part is an identifier for the specific accessor, which might be a user identifying number. Permission: The combination of a subject and an action is a permission. So, for example, being able to download files from a particular folder in a file repository would be a permission. Assignment: In RBAC there is never a direct link between an accessor and permission to perform an action on a subject. Instead, accessors are allocated one or more roles. The linking of an accessor and role is an assignment. Role: A role is the bearer of permissions and is similar to the notion of a group. It is roles that are granted one or more permissions. It is easy to see that we can control what can be done by allocating roles to users, and then checking to see if any of a user's roles has a particular permission. Moreover, we can generalize this beyond users to other types of accessor as the need arises. The model built so far is known in the academic literature as RBAC0. Adding Hierarchy As RBAC can operate at a much more general level than ACL, it will often happen that one role embraces another. Suppose we think of the example of a hospital, the role of consultant might include the role of doctor. Not everyone who has the role of doctor would have the role of consultant. But all consultants are doctors. At present, Aliro implements hierarchy purely for backwards compatibility with the Mambo, and Joomla! schemes, where there is a strict hierarchy of roles for ACL. The ability to extend hierarchy more generally is feasible, given the Aliro implementation, and may be added at some point. The model with the addition of role hierarchies is known as RBAC1. Adding Constraints In general data processing, situations arise where RBAC is expected to implement constraints on the allocation of roles. A typical example would be that the same person is not permitted to have both purchasing and account manager roles. Restrictions of this kind derive from fairly obvious principles to limit scope for fraud. While constraints can be powerful additions to RBAC, they do not often arise in web applications, so Aliro does not presently provide any capability for constraints. The option is not precluded, since constraints are typically grafted on top of an RBAC system that does not have them. Adding constraints to the basic RBAC0 model creates an RBAC2 model, and if both hierarchy and constraints are provided, the model is called RBAC3. Avoiding Unnecessary Restrictions When it comes to design an implementation, it would be a pity to create obstacles that will be troublesome later. To achieve maximum flexibility, few restrictions are placed on the information that is stored by the RBAC system. Subjects and accessors have both types, and identifiers. The types can be strings, and there is no need for the RBAC system to limit what can be used in this respect. A moderate limitation on length is not unduly restrictive. It is up to the wider CMS to decide, for example, what kinds of subjects are needed. Our example for this article is the file repository, and the subjects it needs are known to the designer of the repository. All requests to the RBAC system from the file repository will take account of this knowledge. Identifiers will often be simple numbers, probably derived from an auto-increment primary key in the database. But it would be unduly restrictive to insist that identifiers must be numbers. It may be that control is needed over subjects that cannot be identified by a number. Maybe the subject can only be identified by a non-numeric key such as a URI, or maybe it needs more than one field to pick it out. For these reasons, it is better to implement the RBAC system with the identifiers as strings, possibly with quite generous length constraints. That way, the designers of software that makes use of the RBAC system have the maximum opportunity to construct identifiers that work in a particular context. Any number of schemes can be imagined that will combine multiple fields into a string; after all, the only thing we will do with the identifier in the RBAC system is to test for equality. Provided identifiers are unique, their precise structure does not matter. The only point to watch is making sure that whatever the original identifier may be, it is consistently converted into a string. Actions can be simple strings, since they are merely arbitrary labels. Again, their meaning is important only within the area that is applying RBAC, so the actual RBAC system does not need to impose any restrictions. Length need not be especially large. Roles are similar, although systems sometimes include a table of roles because extra information is held, such as a description of the role. But since this is not really a requirement of RBAC, the system built here will not demand descriptions for roles, and will permit a role to be any arbitrary string. While descriptions can be useful, it is easy to provide them as an optional extra. Avoiding making them a requirement keeps the system as flexible as possible, and makes it much easier to create roles on the fly, something that will often be needed. Some Special Roles Handling access controls can be made easier and more efficient by inventing some roles that have their own special properties. Aliro uses three of these: visitor, registered, and nobody. Everyone who comes to the site is counted as a visitor, and is therefore implicitly given the role visitor. If a right is granted to this role, it is assumed that it is granted to everybody. After all, it is illogical to give a right to a visitor, and deny it to a user who has logged in, since the user could gain the access right just by logging out. For the sake of efficient implementation of the visitor role, two things are done. One is that nothing is stored to associate particular users with the role, since everyone has it automatically. Second, since most sites offer quite a lot of access to visitors prior to login, the visitor role is given access to anything that has not been connected with some more specific role. This means, again, that nothing needs to be stored in relation to the visitor role. Almost as extensive is the role registered, which is automatically applied to anyone who has logged in, but excludes visitors who have not logged in. Again, nothing is stored to associate users with the role, since it applies to anyone who identifies themselves as a registered user. But in this case, rights can be granted to the registered role. Rather like the visitor role, logic dictates that if access is granted to all registered users, any more specific rights are redundant, and can be ignored. Finally, the role of "nobody" is useful because of the principle that where no specific access has been granted, a resource is available to everyone. Where all access is to be blocked, then access can be granted to "nobody" and no user is permitted to be "nobody". In fact, we can now see that no user can be allocated to any of the special roles since they are always linked to them automatically or not at all. Implementation Efficiency Clearly an RBAC system may have to handle a lot of data. More significantly, it may need to deal with a lot of requests in a short time. A page of output will often consist of multiple elements, any or all of which may involve decisions on access. A two pronged approach can be taken to this problem, using two different kinds of cache. Some RBAC data is general in nature, an obvious example being the role hierarchy. This applies equally to everyone, and is a relatively small amount of data. Information of this kind can be cached in the file system so as to be available to every request. Much RBAC information is linked to the particular user. If all such data were to be stored in the standard cache, it is likely that the cache would grow very large, with much of the data irrelevant to any particular request. A better approach is to store RBAC data that is specific to the user as session data. That way, it will be available for every request by the same user, but will not be cluttered up with data for other users. Since Aliro ensures that there is a live session for every user, including visitors who have not yet logged in, and also preserves the session data at login, this is a feasible approach. Where are the Real Difficulties? Maybe you think we already have enough problems to solve without looking for others? The sad fact is that we have not yet even considered the most difficult one! In my experience, the real difficulties arise in trying to design a user interface to deal with actual control requirements. The example used in this article is relatively simple. Controlling what users can do in a file repository extension does not immediately introduce much complexity. But this apparently simple situation is easily made more complex by the kind of requests that are often made for a more advanced repository. In the simple case, all we have to worry about is that we have control over areas of the repository, indicating who can upload, who can download, and who can edit the files. Those are the requirements that are covered by the examples below. Going beyond that, though, consider a situation that is often discussed as a possible requirement. The repository is extended so that some users have their own area, and can do what they like within it. A simple consequence of this is that we need to be able to grant those users the ability to create new folders in the file repository, as well as to upload and edit files in the existing folders. So far so good! But this scenario also introduces the idea that we may want the user who owns an area of the repository to be able to have control over certain areas, which other users may have access to. Now we need the additional ability to control which users have the right to give access to certain parts of the repository. If we want to go even further, we can raise the issue of whether a user in this position would be able to delegate the granting of access in their area to other users, so as to achieve a complete hierarchy of control. Handling the technical requirements here is not too difficult. What is difficult is designing user interfaces to deal with all the possibilities without creating an explosion of complexity. For an individual case it is feasible to find a solution. An attempt to create a general solution would probably result in a problem that would be extremely hard to solve. Summary In this part of the article we had a look at the highly flexible role-based access control system. We established the principles using standard notions of RBAC. We discussed about the specific details, such as the way accessors and subjects are identified are adapted to the particular situation of a CMS framework. In part 2 of the article, we will look at the database implementation and the code for administering RBAC. We will also consider in outline how questions about access can be answered.  
Read more
  • 0
  • 0
  • 2259

article-image-facebook-application-development-ruby-rails
Packt
21 Oct 2009
4 min read
Save for later

Facebook Application Development with Ruby on Rails

Packt
21 Oct 2009
4 min read
Technologies needed for this article RFacebook RFacebook (http://rfacebook.rubyforge.org/index.html) is a Ruby interface to the Facebook APIs. There are two parts to RFacebook—the gem and the plug-in. The plug-in is a stub that calls RFacebook on the Rails library packaged in the gem. RFacebook on Rails library extends the default Rails controller, model, and view. RFacebook also provides a simple interface through an RFacebook session to call any Facebook API. RFacebook uses some meta-programming idioms in Ruby to call Facebook APIs. Indeed Indeed is a job search engine that allows users to search for jobs based on keywords and location. It includes job listings from major job boards and newspapers and even company career pages. Acquiring candidates through Facebook We will be creating a Facebook application and displaying it through Facebook. This application, when added into the list of a user's applications, allows the user to search for jobs using information in his or her Facebook profile. Facebook applications, though displayed within the Facebook interface, are actually hosted and processed somewhere else. To display it within Facebook, you need to host the application in a publicly available website and then register the application. We will go through these steps in creating the Job Board Facebook application. Creating a Rails application Next, create a Facebook application. To do this, you will need to first add a special application in your Facebook account—the Developer application. Go to http://www.facebook.com/developers and you will be asked to allow Developer to be installed in your Facebook account. Add the Developer application and agree to everything in the permissions list. You will not have any applications yet, so click on the create one link to create a new application. Next you will be asked for the name of the application you want to create. Enter a suitable name; in our case, enter 'Job Board' and you will be redirected to the Developer application main page, where you are shown your newly created application with its API key and secret. You will need the API key and secret in a while. Installing and configuring RFacebook RFacebook consists of two components—the gem and the plug-in. The gem contains the libraries needed to communicate with Facebook while the plug-in enables your Rails application to integrate with Facebook. As mentioned earlier, the plug-in is basically a stub to the gem. The gem is installed like any other gem in Ruby: $gem install rfacebook To install the plug-in go to your RAILS_ROOT folder and type in: $./script/plugin install svn://rubyforge.org/var/svn/rfacebook/trunk/rfacebook/plugins/rfacebook Next, after the gem and plug-in is installed, run a setup rake script to create the configuration file in the RAILS_ROOT folder: $rake facebook:setup This creates a facebook.yml configuration file in RAILS_ROOT/config folder. The facebook.yml file contains three environments that mirror the Rails startup environments. Open it up to configure the necessary environment with the API key and secret that you were given when you created the application in the section above. development: key: YOUR_API_KEY_HERE secret: YOUR_API_SECRET_HERE canvas_path: /yourAppName/ callback_path: /path/to/your/callback/ tunnel: username: yourLoginName host: www.yourexternaldomain.com port: 1234 local_port: 5678 For now, just fill in the API key and secret. In a later section when we configure the rest of the Facebook application, we will need to revisit this configuration. Extracting the Facebook user profile Next we want to extract the user's Facebook user profile and display it on the Facebook application. We do this to let the user confirm that this is the information he or she wants to send as search parameters. To do this, create a controller named search_controller.rb in the RAILS_ROOT/app/controllers folder. class SearchController < ApplicationController before_filter :require_facebook_install layout 'main' def index view render :action => :view end def view if fbsession.is_valid? response = fbsession.users_getInfo(:uids => [fbsession.session_user_id], :fields => ["current_location", "education_history", "work_history"]) @work_history = response.work_history @education_history = response.education_history @current_location = response.current_location endend
Read more
  • 0
  • 0
  • 3936

article-image-apache-ofbiz-service-engine-part-1
Packt
21 Oct 2009
6 min read
Save for later

Apache OFBiz Service Engine: Part 1

Packt
21 Oct 2009
6 min read
Defining a Service We first need to define a service. Our first service will be named learningFirstService. In the folder ${component:learning}, create a new folder called servicedef. In that folder, create a new file called services.xml and enter into it this: <?xml version="1.0" encoding="UTF-8" ?> <services xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/services.xsd"> <description>Learning Component Services</description> <service name="learningFirstService" engine="java" location="org.ofbiz.learning.learning.LearningServices" invoke="learningFirstService"> <description>Our First Service</description> <attribute name="firstName" type="String" mode="IN" optional="true"/> <attribute name="lastName" type="String" mode="IN" optional="true"/> </service> </services> In the file ${component:learning}ofbiz-component.xml, add after the last <entity-resource> element this: <service-resource type="model" loader="main" location="service def/services.xml"/> That tells our component learning to look for service definitions in the file ${component:learning}servicedefservices.xml. It is important to note that all service definitions are loaded at startup; therefore any changes to any of the service definition files will require a restart! Creating the Java Code for the Service In the package org.ofbiz.learning.learning, create a new class called LearningServices with one static method learningFirstService: package org.ofbiz.learning.learning; import java.util.Map; import org.ofbiz.service.DispatchContext; import org.ofbiz.service.ServiceUtil; public class LearningServices { public static final String module = LearningServices.class.getName(); public static Map learningFirstService(DispatchContext dctx, Map context){ Map resultMap = ServiceUtil.returnSuccess("You have called on service 'learningFirstService' successfully!"); return resultMap; } } Services must return a map. This map must contain at least one entry. This entry must have the key responseMessage (see org.ofbiz.service.ModelService.RESPONSE_MESSAGE), having a value of one of the following: success or ModelService.RESPOND_SUCCESS error or ModelService.RESPOND_ERROR fail or ModelService.RESPOND_FAIL By using ServiceUtil.returnSuccess() to construct the minimal return map, we do not need to bother adding the responseMessage key and value pair. Another entry that is often used is that with the key successMessage (ModelService.SUCCESS_MESSAGE). By doing ServiceUtil.returnSuccess("Some message"), we will get a return map with entry successMessage of value "Some message". Again, ServiceUtil insulates us from having to learn the convention in key names. Testing Our First Service Stop OFBiz, recompile our learning component and restart OFBiz so that the modified ofbiz-component.xml and the new services.xml can be loaded. In ${component:learning}widgetlearningLearningScreens.xml, insert a new Screen Widget: <screen name="TestFirstService"> <section> <widgets> <section> <condition><if-empty field-name="formTarget"/></condition> <actions> <set field="formTarget" value="TestFirstService"/> <set field="title" value="Testing Our First Service"/> </actions> <widgets/> </section> <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}"> <decorator-section name="body"> <include-form name="TestingServices" location="component://learning/widget/learning/LearningForms.xml"/> <label text="Full Name: ${parameters.fullName}"/> </decorator-section> </decorator-screen> </widgets> </section> </screen> In the file ${component:learning}widgetlearningLearningForms.xml, insert a new Form Widget: <form name="TestingServices" type="single" target="${formTarget}"> <field name="firstName"><text/></field> <field name="lastName"><text/></field> <field name="planetId"><text/></field> <field name="submit"><submit/></field> </form> Notice how the formTarget field is being set in the screen and used in the form. For now don't worry about the Full Name label we are setting from the screen. Our service will eventually set that. In the file ${webapp:learning}WEB-INFcontroller.xml, insert a new request map: <request-map uri="TestFirstService"> <event type="service" invoke="learningFirstService"/> <response name="success" type="view" value="TestFirstService"/> </request-map> The control servlet currently has no way of knowing how to handle an event of type service, so in controller.xml we must add a new handler element immediately under the other <handler> elements: <handler name="service" type="request" class="org.ofbiz.webapp.event.ServiceEventHandler"/> <handler name="service-multi" type="request" class="org.ofbiz.webapp.event.ServiceMultiEventHandler"/> We will cover service-multi services later. Finally add a new view map: <view-map name="TestFirstService" type="screen" page="component://learning/widget/learning/LearningScreens.xml#TestFirstService"/> Fire to webapp learning an http OFBiz request TestFirstService, and see that we have successfully invoked our first service: Service Parameters Just like Java methods, OFBiz services can have input and output parameters and just like Java methods, the parameter types must be declared. Input Parameters (IN) Our first service is defined with two parameters: <attribute name="firstName" type="String" mode="IN" optional="true"/> <attribute name="lastName" type="String" mode="IN" optional="true"/> Any parameters sent to the service by the end-user as form parameters, but not in the services list of declared input parameters, will be dropped. Other parameters are converted to a Map by the framework and passed into our static method as the second parameter. Add a new method handleInputParamaters to our LearningServices class. public static Map handleParameters(DispatchContext dctx, Map context){ String firstName = (String)context.get("firstName"); String lastName = (String)context.get("lastName"); String planetId= (String)context.get("planetId"); String message = "firstName: " + firstName + "<br/>"; message = message + "lastName: " + lastName + "<br/>"; message = message + "planetId: " + planetId; Map resultMap = ServiceUtil.returnSuccess(message); return resultMap; } We can now make our service definition invoke this method instead of the learningFirstService method by opening our services.xml file and replacing: <service name="learningFirstService" engine="java" location="org.ofbiz.learning.learning.LearningServices" invoke="learningFirstService"> with: <service name="learningFirstService" engine="java" location="org.ofbiz.learning.learning.LearningServices" invoke="handleParameters"> Once again shutdown, recompile, and restart OFBiz. Enter for fields First Name, Last Name, and Planet Id values Some, Name, and Earth, respectively. Submit and notice that only the first two parameters went through to the service. Parameter planetId was dropped silently as it was not declared in the service definition. Modify the service learningFirstService in the file ${component:learning}servicedefservices.xml, and add below the second parameter a third one like this: <attribute name="planetId" type="String" mode="IN" optional="true"/> Restart OFBiz and submit the same values for the three form fields, and see all three parameters go through to the service.  
Read more
  • 0
  • 0
  • 3399
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at €18.99/month. Cancel anytime
article-image-configuring-jdbc-oracle-jdeveloper
Packt
21 Oct 2009
14 min read
Save for later

Configuring JDBC in Oracle JDeveloper

Packt
21 Oct 2009
14 min read
Introduction Unlike Eclipse IDE, which requires a plug-in, JDeveloper has a built-in provision to establish a JDBC connection with a database. JDeveloper is the only Java IDE with an embedded application server, the Oracle Containers for J2EE (OC4J). This database-based web application may run in JDeveloper without requiring a third-party application server. However, JDeveloper also supports third-party application servers. Starting with JDeveloper 11, application developers may point the IDE to an application server instance (or OC4J instance), including third-party application servers that they want to use for testing during development. JDeveloper provides connection pooling for the efficient use of database connections. A database connection may be used in an ADF BC application, or in a JavaEE application. A database connection in JDeveloper may be configured in the Connections Navigator. A Connections Navigator connection is available as a DataSource registered with a JNDI naming service. The database connection in JDeveloper is a reusable named connection that developers configure once and then use in as many of their projects as they want. Depending on the nature of the project and the database connection, the connection is configured in the bc4j.xcfg file or a JavaEE data source. Here, it is necessary to distinguish between data source and DataSource. A data source is a source of data; for example an RDBMS database is a data source. A DataSource is an interface that represents a factory for JDBC Connection objects. JDeveloper uses the term Data Source or data source to refer to a factory for connections. We will also use the term Data Source or data source to refer to a factory for connections, which in the javax.sql package is represented by the DataSource interface. A DataSource object may be created from a data source registered with the JNDI (Java Naming and Directory) naming service using JNDI lookup. A JDBC Connection object may be obtained from a DataSource object using the getConnection method. As an alternative to configuring a connection in the Connections Navigator a data source may also be specified directly in the data source configuration file data-sources.xml. In this article we will discuss the procedure to configure a JDBC connection and a JDBC data source in JDeveloper 10g IDE. We will use the MySQL 5.0 database server and MySQL Connector/J 5.1 JDBC driver, which support the JDBC 4.0 specification. In this article you will learn the following: Creating a database connection in JDeveloper Connections Navigator. Configuring the Data Source and Connection Pool associated with the connection configured in the Connections Navigator. The common JDBC Connection Errors. Before we create a JDBC connection and a data source we will discuss connection pooling and DataSource. Connection Pooling and DataSource The javax.sql package provides the API for server-side database access. The main interfaces in the javax.sql package are DataSource, ConnectionPoolDataSource, and PooledConnection. The DataSource interface represents a factory for connections to a database. DataSource is a preferred method of obtaining a JDBC connection. An object that implements the DataSource interface is typically registered with a Java Naming and Directory API-based naming service. DataSource interface implementation is driver-vendor specific. The DataSource interface has three types of implementations: Basic implementation: In basic implementation there is 1:1 correspondence between a client's Connection object and the connection with the database. This implies that for every Connection object, there is a connection with the database. With the basic implementation, the overhead of opening, initiating, and closing a connection is incurred for each client session. Connection pooling implementation: A pool of Connection objects is available, from which connections are assigned to the different client sessions. A connection pooling manager implements the connection pooling. When a client session does not require a connection, the connection is returned to the connection pool and becomes available to other clients. Thus, the overheads of opening, initiating, and closing connections are reduced. Distributed transaction implementation: Distributed transaction implementation produces a Connection object that is mostly used for distributed transactions and is always connection pooled. A transaction manager implements the distributed transactions. An advantage of using a data source is that code accessing a data source does not have to be modified when an application is migrated to a different application server. Only the data source properties need to be modified. A JDBC driver that is accessed with a DataSource does not register itself with a DriverManager. A DataSource object is created using a JNDI lookup and subsequently a Connection object is created from the DataSource object. For example, if a data source JNDI name is jdbc/OracleDS a DataSource object may be created using JNDI lookup. First, create an InitialContext object and subsequently create a DataSource object using the InitialContext lookup method. From the DataSource object create a Connection object using the getConnection() method: InitialContext ctx=new InitialContext(); DataSource ds=ctx.lookup("jdbc/OracleDS"); Connection conn=ds.getConnection(); The JNDI naming service, which we used to create a DataSource object is provided by J2EE application servers such as the Oracle Application Server Containers for J2EE (OC4J) embedded in the JDeveloper IDE. A connection in a pool of connections is represented by the PooledConnection interface, not the Connection interface. The connection pool manager, typically the application server, maintains a pool of PooledConnection objects. When an application requests a connection using the DataSource.getConnection() method, as we did using the jdbc/OracleDS data source example, the connection pool manager returns a Connection object, which is actually a handle to an object that implements the PooledConnection interface. A ConnectionPoolDataSource object, which is typically registered with a JNDI naming service, represents a collection of PooledConnection objects. The JDBC driver provides an implementation of the ConnectionPoolDataSource, which is used by the application server to build and manage a connection pool. When an application requests a connection, if a suitable PooledConnection object is available in the connection pool, the connection pool manager returns a handle to the PooledConnection object as a Connection object. If a suitable PooledConnection object is not available, the connection pool manager invokes the getPooledConnection() method of the ConnectionPoolDataSource to create a new PooledConnection object. For example, if connectionPoolDataSource is a ConnectionPoolDataSource object a new PooledConnection gets created as follows: PooledConnection pooledConnection=connectionPoolDataSource.getPooledConnection(); The application does not have to invoke the getPooledConnection() method though; the connection pool manager invokes the getPooledConnection() method and the JDBC driver implementing the ConnectionPoolDataSource creates a new PooledConnection, and returns a handle to it. The connection pool manager returns a Connection object, which is a handle to a PooledConnection object, to the application requesting a connection. When an application closes a Connection object using the close() method, as follows, the connection does not actually get closed. conn.close(); The connection handle gets deactivated when an application closes a Connection object with the close() method. The connection pool manager does the deactivation. When an application closes a Connection object with the close() method any client info properties that were set using the setClientInfo method are cleared. The connection pool manager is registered with a PooledConnection object using the addConnectionEventListener() method. When a connection is closed, the connection pool manager is notified and the connection pool manager deactivates the handle to the PooledConnection object, and returns the PooledConnection object to the connection pool to be used by another application. The connection pool manager is also notified if a connection has an error. A PooledConnection object is not closed until the connection pool is being reinitialized, the server is shutdown, or a connection becomes unusable. In addition to connections being pooled, PreparedStatement objects are also pooled by default if the database supports statement pooling. It can be discovered if a database supports statement pooling using the supportsStatementPooling() method of the DatabaseMetaData interface. The PeparedStatement pooling is also managed by the connection pool manager. To be notified of PreparedStatement events such as a PreparedStatement getting closed or a PreparedStatement becoming unusable, a connection pool manager is registered with a PooledConnection manager using the addStatementEventListener() method. A connection pool manager deregisters a PooledConnection object using the removeStatementEventListener() method. Methods addStatementEventListener and removeStatementEventListener are new methods in the PooledConnection interface in JDBC 4.0. Pooling of Statement objects is another new feature in JDBC 4.0. The Statement interface has two new methods in JDBC 4.0 for Statement pooling: isPoolable() and setPoolable(). The isPoolable method checks if a Statement object is poolable and the setPoolable method sets the Statement object to poolable. When an application closes a PreparedStatement object using the close() method the PreparedStatement object is not actually closed. The PreparedStatement object is returned to the pool of PreparedStatements. When the connection pool manager closes a PooledConnection object by invoking the close() method of PooledConnection all the associated statements also get closed. Pooling of PreparedStatements provides significant optimization, but if a large number of statements are left open, it may not be an optimal use of resources. Thus, the following procedure is followed to obtain a connection in an application server using a data source: Create a data source with a JNDI name binding to the JNDI naming service. Create an InitialContext object and look up the JNDI name of the data source using the lookup method to create a DataSource object. If the JDBC driver implements the DataSource as a connection pool, a connection pool becomes available. Request a connection from the connection pool. The connection pool manager checks if a suitable PooledConnection object is available. If a suitable PooledConnection object is available, the connection pool manager returns a handle to the PooledConnection object as a Connection object to the application requesting a connection. If a PooledConnection object is not available the connection pool manager invokes the getPooledConnection() method of the ConnectionPoolDataSource, which is implemented by the JDBC driver. The JDBC driver implementing the ConnectionPoolDataSource creates a PooledConnection object and returns a handle to it. The connection pool manager returns a handle to the PooledConnection object as a Connection object to the application requesting a connection. When an application closes a connection, the connection pool manager deactivates the handle to the PooledConnection object and returns the PooledConnection object to the connection pool. ConnectionPoolDataSource provides some configuration properties to configure a connection pool. The configuration pool properties are not set by the JDBC client, but are implemented or augmented by the connection pool. The properties can be set in a data source configuration. Therefore, it is not for the application itself to change the settings, but for the administrator of the pool, who also happens to be the developer sometimes, to do so. Connection pool properties supported by ConnectionPoolDataSource are discussed in following table: Connection Pool Property Type Description maxStatements int Maximum number of statements the pool should keep open. 0 (zero) indicates that statement caching is not enabled. initialPoolSize int The initial number of connections the pool should have at the time of creation. minPoolSize int The minimum number of connections in the pool. 0 (zero) indicates that connections are created as required. maxPoolSize int The maximum number of connections in the connection pool. 0 indicates that there is no maximum limit. maxIdleTime int Maximum duration (in seconds) a connection can be kept open without being used before the connection is closed. 0 (zero) indicates that there is no limit. propertyCycle int The interval in seconds the pool should wait before implementing the current policy defined by the connection pool properties. maxStatements int The maximum number of statements the pool can keep open. 0 (zero) indicates that statement caching is not enabled.     Setting the Environment Before getting started, we have to install the JDeveloper 10.1.3 IDE and the MySQL 5.0 database. Download JDeveloper from: http://www.oracle.com/technology/software/products/jdev/index.html. Download the MySQL Connector/J 5.1, the MySQL JDBC driver that supports JDBC 4.0 specification. To install JDeveloper extract the JDeveloper ZIP file to a directory. Log in to the MySQL database and set the database to test. Create a database table, Catalog, which we will use in a web application. The SQL script to create the database table is listed below: CREATE TABLE Catalog(CatalogId VARCHAR(25)PRIMARY KEY, Journal VARCHAR(25), Publisher VARCHAR(25),Edition VARCHAR(25), Title Varchar(45), Author Varchar(25));INSERT INTO Catalog VALUES('catalog1', 'Oracle Magazine', 'Oracle Publishing', 'Nov-Dec 2004', 'Database Resource Manager', 'Kimberly Floss');INSERT INTO Catalog VALUES('catalog2', 'Oracle Magazine', 'Oracle Publishing', 'Nov-Dec 2004', 'From ADF UIX to JSF', 'Jonas Jacobi'); MySQL does not support ROWID, for which support has been added in JDBC 4.0. Having installed the JDeveloper IDE, next we will configure a JDBC connection in the Connections Navigator. Select the Connections tab and right-click on the Database node to select New Database Connection. Click on Next in Create Database Connection Wizard. In the Create Database Connection Type window, specify a Connection Name—MySQLConnection for example—and set Connection Type to Third Party JDBC Driver, because we will be using MySQL database, which is a third-party database for Oracle JDeveloper and click on Next. If a connection is to be configured with Oracle database select Oracle (JDBC) as the Connection Type and click on Next. In the Authentication window specify Username as root (Password is not required to be specified for a root user by default), and click on Next. In the Connection window, we will specify the connection parameters, such as the driver name and connection URL; click on New to specify a Driver Class. In the Register JDBC Driver window, specify Driver Class as com.mysql.jdbc.Driver and click on Browse to select a Library for the Driver Class. In the Select Library window, click on New to create a new library for the MySQL Connector/J 5.1 JAR file. In the Create Library window, specify Library Name as MySQL and click on Add Entry to add a JAR file entry for the MySQL library. In the Select Path Entry window select mysql-connector-java-5.1.3-rcmysql-connector-java-5.1.3-rc-bin.jar and click on Select. In the Create Library window, after a Class Path entry gets added to the MySQL library, click on OK. In the Select Library window, select the MySQL library and click on OK. In the Register JDBC Driver window, the MySQL library gets specified in the Library field and the mysql-connector-java-5.1.3-rcmysql-connector-java-5.1.3-rc-bin.jar gets specified in the Classpath field. Now, click on OK. The Driver Class, Library, and Classpath fields get specified in the Connection window. Specify URL as jdbc:mysql://localhost:3306/test, and click on Next. In the Test window click on Test Connection to test the connection that we have configured. A connection is established and a success message gets output in the Status text area. Click on Finish in the Test window. A connection configuration, MySQLConnection, gets added to the Connections navigator. The connection parameters are displayed in the structure view. To modify any of the connection settings, double-click on the Connection node. The Edit Database Connection window gets displayed. The connection Username, Password, Driver Class, and URL may be modified in the Edit window. A database connection configured in the Connections navigator has a JNDI name binding in the JNDI naming service provided by OC4J. Using the JNDI name binding, a DataSource object may be created in a J2EE application. To view, or modify the configuration settings of the JDBC connection select Tools | Embedded OC4J Server Preferences in JDeveloper. In the window displayed, select Global | Data Sources node, and to update the data-sources.xml file with the connection defined in the Connections navigator, click on the Refresh Now button. Checkboxes may be selected to Create data-source elements where not defined, and to Update existing data-source elements. The connection pool and data source associated with the connection configured in the Connections navigator get listed. Select the jdev-connection-pool-MySQLConnection node to list the connection pool properties as Property Set A and Property Set B. The tuning properties of the JDBC connection pool may be set in the Connection Pool window. The different tuning attributes are listed in following table:        
Read more
  • 0
  • 0
  • 10382

article-image-radrails-views
Packt
21 Oct 2009
6 min read
Save for later

RadRails Views

Packt
21 Oct 2009
6 min read
Opening the RadRails Views Some of the views that we will go through in this article are available as part of the Rails default perspective, which means you don't need to do anything special to open them; they will appear as tabbed views in a pane at the bottom of your workbench. Just look for the tab name of the view you want to see and click on it to make it visible. However, there are some views that are not opened by default, or maybe you closed them at some point accidentally, or maybe you changed to the Debug perspective and you want to display some of the RadRails views there. When you need to open a view whose tab is not displaying, you can go to the Window menu, and select the Show View option. If you are in the Rails perspective, all the available views will be displayed in that menu, as you can see in the screenshot above. When opening this menu from a different perspective, you will not see the RadRails views here, but you can select Other.... If this is the case, in the Show View dialog, most of the views will appear under the Ruby category, except for the Generators, Rails API, and Rake Tasks views, which are located under Rails. Documentation Views As happens with any modern programming language, Ruby has an extensive API. There are lots of libraries and classes and even with Ruby being an intuitive language with a neat consistent API, often we need to read the documentation. As you probably know, Ruby provides a standard documentation format called RDoc, which uses the comments in the source code to generate documentation. We can access this RDoc documentation in different ways, mainly in HTML format through a browser or by using the command-line tool RI. This produces a plain-text output directly at the command shell, in a similar way to the man command in a UNIX system. RadRails doesn't add any new functionality to the built-in documentation, but provides some convenient views so we can explore it without losing the context of our project's source. Ruby Interactive (RI) View This view provides a fast and comfortable way of browsing the local documentation in the same way as you would use RI from the command line. You can look either for a class or a method name. Just start typing at the input box at the top left corner of the view and the list below will display the matching entries. That's a nice improvement over the command line interface, since you can see the results as you type instead of having to run a complete search every time. If you know the name of both the class and the method you are looking for, then you can write them using the hash (pound) sign as a separator. For example, to get the documentation for the sum method of the class Enumerable you would write Enumerable#sum. The documentation will display in the right pane, with a convenient highlighting of the referenced methods and classes. Even if the search results of RI don't look very attractive compared to the output of the HTML-based documentation views, RI has the advantage of searching locally on your computer, so you can use it even when working off-line. Ruby Core, Ruby Standard Library, and Rails API There are three more views related to documentation in RadRails: Ruby Core API, Ruby Standard Library API, and Rails API. Unlike the RI view, these ones look for the information over the Internet, so you will not be able to use them unless you are on-line. On the other hand, the information is displayed in a more attractive way than with RI, and it provides links to the source code of the consulted methods, so if the documentation is not enough, you can always take a look at the inner details of the implementation. The Ruby Core API view displays the documentation of the classes included in Ruby's core. These are the classes you can directly use without a previous require statement. The documentation rendered is that at http://www.ruby-doc.org/core/. You are probably familiar with this type of layout, since it's the default RDoc output. The upper pane displays the navigation links, and the lower pane shows the detail of the documentation. The navigation is divided into three frames. The one to the left shows the files in which the source code is, the one in the middle shows the Classes and Modules, and in the third one you can find all the methods in the API. The Ruby Standard Library API is composed of all the classes and modules that are not a part of Ruby's core, but are typically distributed as a part of the Ruby installation. You can directly use these classes after a require statement in your code. The Ruby Standard Library API View displays the information from http://www.ruby-doc.org/stdlib. In this case, the navigation is the same as in Ruby Core, but with an additional area to the left, in which you can see all the available packages (the ones you would require for using the classes within your code). When you select a package link, you will see the files, classes, and methods for that single package. The last of the documentation views displays information about the Rails API. It includes the documentation of ActiveRecord, the ActionPack, ActiveSupport, and the rest of the Rails components. The information is obtained from http://api.rubyonrails.org. In this case the layout is slightly different because the information about the files, classes, and methods is displayed to the left instead at the top of the view. Apart from that, the behavior is identical to that of the Ruby Core API view. Since some of the API descriptions are fairly long, it can be convenient to maximize the documentation views when you are using them. Remember you can maximize any of the views by double-clicking its tab or by using the maximize icon on the view's toolbar. Double-clicking again will restore the view to the original size and position.
Read more
  • 0
  • 0
  • 2953

article-image-change-control-personal-projects-subversion-style
Packt
21 Oct 2009
5 min read
Save for later

Change Control for Personal Projects - Subversion Style

Packt
21 Oct 2009
5 min read
Who Should Read This Read on if you are new to change control, or believe that change control only applies to software, or that it is only meant for large projects. If you are a software pro working with large software projects, you can still read this if you want a gentle introduction to Subversion or svn as it is called. Introduction We have all heard those trite remarks about change -- “... change is the only constant ...”, or similar ones, especially before an unpleasant corporate announcement. These overused remarks about change are unfortunately true. During the course of a day, we make numerous (hopefully!) interrelated changes, updates, or transformations to our work products to reach specific project goals. Needless to say, these changes need to be tracked along with the rationale behind each if we are to prevent ourselves from repeating mistakes, or simply want to recall why we did what we did one month ago! Note that we are not talking about only code or documents here; your work products could be a portfolios of photographs, animations, or some arbitrary binary format. A change control discipline also gives you additional advantages such as being able to develop simultaneous versions of work products for different purposes or clients, rolling back to a previous arbitrary version, or setting up trial development in a so-called branch to bring it back to the main work stream after due review. You also have a running history of how your work product has evolved over time and features. Fetching from a change managed repository also prevents you from creating those fancifully named multiple copies of a file just to keep track of its versions. To reiterate: we use the words 'work product' and 'development' in the broadest sense and not just as applied to software. You might as well be creating a banner ad for your client as much as a Firefox plugin. In the rest of this article we will see how to build a simple personal change control discipline for your day-to-day work using a version control tool. As you will note, 'control' and 'management' have been used interchangeably, though a little hair splitting will yield rich dividends in terms of how different these terms are. Subversion Subversion is version control system available on the Linux (and similar) platforms. If you are trapped in a proprietary world by choice, circumstance, or compulsion, you should try TortoiseSVN. Here, we confine ourselves to the Linux platform. Subversion works by creating a time line of your work products from their inception (or from the point they are brought under version control) to the present point in time, by capturing snapshots of your work products at discrete points that you decide. Each snapshot is a version. You can traverse this time line and extract specific versions for use. How does subversion do it? It versions entire directories. A new version of your directory will be created even if you change one file in it. Don't worry; this does not lead to an explosion of file size with each version. Explaining some terminology, albeit informally, should make the going easier from here. Subversion stores your project(s) in a repository. For the purpose of this article, our repository will stay on the local machine. A revision is nothing but a particular snapshot of the project directory. A working directory is your sandbox. This is where you check out a particular version of your project directory from the repository, make any modifications to it, and then do a check in back into the repository. Revision numbers are bumped up with each check in. You can revert a configuration item, which is like undoing any changes you made. If all this sounds a little abstruse, don't worry, because we will shortly set up our repository so that you can try things out. A commit is when you...., well commit a change done to a file into the repository. Subversion is mostly bundled with a Linux distribution. Find out if you have yours with a 'man svn' or 'svn -h' or a 'whereis svn' command. Setting up Your Repository You can set up your repository in your home directory if you are working on a shared environment. If you have a machine to yourself, you might want to create an 'svn' account with /sbin/nologin (politely refuses logins) as the shell. Your repository might then be '/home/svn/repos'. Subversion is a command line tool. But the only command you will ever issue for the purpose of this article will be to set up your repository: $ svnadmin create /path/to/your/repository The rest, as they say, is GUI! Let Us Get Visual A GUI for subversion is a great tool for learning and working even if you decide to settle for the command line once you get more proficient. eSvn (http://zoneit.free.fr/esvn/) is a Qt-based graphical front end for Subversion. Follow the instructions with the download to compile and install eSvn. Run esvn and this is how it will look with the File | Options... dialog open. Make sure you enter the correct path to svn if not for the other items.    
Read more
  • 0
  • 0
  • 1793

article-image-date-and-calendar-module-drupal-5-part-2
Packt
21 Oct 2009
6 min read
Save for later

Date and Calendar Module in Drupal 5: Part 2

Packt
21 Oct 2009
6 min read
Recipe 40: Timeline Ingredients Timeline module: http://www.drupal.org/project/timeline Timeline widget:http://www.miccolis.net/random/timeline-1-2.tar.gz The Timeline module implements a nice interactive script developed originally by the SIMILE project at MIT. The project "graduated" in 2008, to become its own stand-alone open source project, hosted at Google Code. Log on to the host and enter the following commands. This is almost a garden-variety module installation, however, we also need to download all of the SIMILE timeline code and place it in an api subdirectory. The Timeline widget tarball expands to the timeline-1-2 directory, so in the last step we use the mv command to rename it to api. cd home/DRUPALACCOUNT/public_html/sites/all/moduleswget http://ftp.drupal.org/files/projects/timeline-5.x-1.0.tar.gztar xvf timeline-5.x-1.0.tar.gzcd timeline/wget http://www.miccolis.net/random/timeline-1-2.tar.gz tar xvf timeline-1-2.tar.gz mv timeline-1-2 api Enable the Timeline module. Explore the configuration settings at admin/settings/timeline. Here you may set the width, height, and initial date focus. Also note the Advanced settings for the path to the Timeline widget JavaScript. If, for some reason, you are unsuccessful making the widget available, you may temporarily try pointing to: http://simile.mit.edu/timeline/api/timeline-api.js. (This will work for as long as MIT continues to host this.) Read the README.txt file, and INSTALL.txt file (this is always a good practice when installing new modules). This is a summary of the Views field requirements for setting up a timeline in Drupal. If present If not present First field Date or Event determines location on the timeline Defaults to Node Created Time Second Field Title, with clickable link Node's default title is used Third Field Description Teaser, if available. (CCK Date fields do not have timeline) Additional Fields Additional fields are not used by Timeline. Edit the timeline view created in Recipe 34. In the Page fieldset, change the View Type to Timeline: Horizontal. Scroll down to the Fields fieldset. Note that in our original view, the Title is the first field followed by Date as the second field. We will reverse this. Click on the up arrow in the Date row to Move this item to the top. Add the Text: Description (field_workshop_description) as the third field. Save the view and browse to timeline. Move the cursor around the timeline. Click on the events to see the associated pop-up box. Click and drag to the left to go forward in time. Click and drag works in both the upper and lower timeline bands. Configuration settings for individual timelines are available at admin/build/timeline (these settings will override default settings at admin/settings/timeline). A particularly interesting setting is the one to enable controls. With controls enabled, the user will have the opportunity to filter, or highlight the timeline based on text either in the title or the description. Regular expressions will also work when filtering or highlighting timeline items. Cooking|Recipes Find items that contain either Cooking or Recipes in the title or description ^L Find titles that start with L asagna Find Lasagna-partial words work fine. hea[rl]t Find Health or Heart The searches are case-insensitive. Recipe notes For examples of basic and sophisticated SIMILE Timelines see http://simile.mit.edu/timeline/examples/. More information about Timeline is available at http://code.google.com/p/simile-widgets/ including a mailing list, a wiki, and an independent issue queue for various SIMILE web widgets. There is also an archived listerv available at http://www.nabble.com/SIMILE---General-f27660.html. The listserv remains active for other SIMILE projects but does not accept content regarding "graduated" projects such as Timeline. However, you may find older posts helpful. If you like Timeline, try some of the other SIMILE projects as well, at http://simile.mit.edu/. (Perhaps you can write the next module to make them available in Drupal.) Exposed Filters are incompatible with the Timeline module. Arguments will work. For instance, if you add a Taxonomy item argument the URL: http://YOURSITE.com/timeline/Cooking would show only the workshops in the Cooking category. To incorporate color coding by taxonomy, consider installing the patch at http://drupal.org/node/121298. There is also a nice patch to include icons by taxonomy term at http://drupal.org/node/104890. The Timeline module integrates with the Events module. If no fields are included in the timeline, the timeline displays the date the content was posted. Recipe 41: Views Popup Ingredients Completed Recipe 34, views_popup view Views Popup module: http://drupal.org/project/views_popup The Views Popup module displays a list of up to three fields and then, upon mouseover, displays additional fields in a pop-up box. This is not a date-specific module, but we'll use it to display a workshop description when you move the mouse over the date. We'll also take a look at the views_style_plugins hook, which in this article enabled the Date Browser, Timeline, Calendar, and now List View as Popup View Types. Install and enable the Views Popup module. Open the views_popup view at views_popup/edit. In the Page fieldset, change the View Type to List View as Popup, Show 2 Fields In the Fields fieldset, change the order of the Datestamp and Title fields so that the datestamp is first. Add the Text: Description (field_workshop_description) field. The first two of these fields will display. The third will be hidden by default. Save and view the views_popup view. Note than when you mouse over the date, the description appears Take a look at hook_views_style_plugins in the views_popup.module file: function views_popup_views_style_plugins() { return array( 'list_hint' => array( 'name' => t('List View as Popup, Show 1 field'), 'theme' => 'views_view_list_hint_popup1', 'validate' => 'views_ui_plugin_validate_list', 'needs_fields' => true, 'weight' => -10, ), 'list_hint2' => array( 'name' => t('List View as Popup, Show 2 fields'), 'theme' => 'views_view_list_hint_popup2', 'validate' => 'views_ui_plugin_validate_list', 'needs_fields' => true, 'weight' => -10, ), 'list_hint3' => array( 'name' => t('List View as Popup, Show 3 fields'), 'theme' => 'views_view_list_hint_popup3', 'validate' => 'views_ui_plugin_validate_list', 'needs_fields' => true, 'weight' => -10, ) ); The critical elements in this array are the name and the specified theme callback function. The function appears below in the same file and does the actual work of producing the output, in this case, creating a pop-up. Recipe notes Documentation on hook_views_style_plugins is available in the Drupal handbook at http://drupal.org/node/193448. Think of the style plugin as a way to modify the output of a whole node, while the field formatters apply to a single field.
Read more
  • 0
  • 0
  • 2321
article-image-implementing-document-management-alfresco-3-part1
Packt
21 Oct 2009
5 min read
Save for later

Implementing Document Management in Alfresco 3- part1

Packt
21 Oct 2009
5 min read
Managing spaces A space in Alfresco is nothing but a folder that contains content as well as sub spaces. The space users are the users invited to a space to perform specific actions such as editing content, adding content, discussing a particular document, and so on. The exact capability that a given user has within a space is a function of their role, or rights. Let's consider the capability of creating a sub-space. By default, in order to create a sub-space, one of the following must apply: The user is the administrator of the system The user has been granted the Contributor role The user has been granted the Coordinator role The user has been granted the Collaborator role Similarly, to edit space properties, a user will need to be the administrator or be granted a role that gives them rights to edit the space. These roles include Editor, Collaborator and Coordinator. Space is a smart folder Space is a folder with additional features, such as, security, business rules, workflow, notifications, local search capabilities, and special views. The additional features, which make the space a smart folder, are explained as follows: Space security: You can define security at the space level. You can designate a user or a group of users who can perform certain actions on the content in a space. For example, on the Marketing Communications space in the Intranet, you can specify that only users in the marketing group can add content, and other users can only see the content. Space business rules: Business rules, such as transforming content from Microsoft Word to Adobe PDF and sending notifications when content gets into a space, can be defined at the space level. Space workflow: You can define and manage the content workflow on a space. Typically, you will create a space for the content that needs to be reviewed, and a space for the content that has been approved. You will create various spaces for dealing with the different stages that the work flows through, and Alfresco will manage the movement of the content between those spaces. Space events: Alfresco triggers events when content moves into a space, when content moves out of a space, or when content is modified within a space. You can capture such events at the space level, and trigger certain actions, such as sending email notifications to certain users. Space aspects: Aspects are additional properties and behavior that can be added to the content, based on the space in which it resides. For example, you can define a business rule to add customer details to all of the customer contract documents that are in your intranet's Sales space. Space search: Alfresco search functions can be limited to a space. For example, if you create a space called Marketing, then you can limit the search to documents within the Marketing space, instead of searching the entire site. Space syndication: Content in a space can be syndicated by applying RSS feed scripts to a space. You can apply RSS feeds to your News space, so that other applications and web sites can subscribe to this feed for news updates. Space content: Content in a space can be versioned, locked, checked-in and checked-out, and managed. You can specify certain documents in a space to be versioned, and others not. Space network folder: Space can be mapped to a network drive on your local machine, enabling you to work with the content locally. For example, by using CIFS interface, a space can be mapped to the Windows network folder. Space dashboard view: Content in a space can be aggregated and presented using special dashboard views. For example, the Company Policies space can list all of the latest policy documents, that have been updated in the past one month or so. You can create different views for Sales, Marketing, and Finance departmental spaces. Why space hierarchy is important Like regular folders, a space can have child spaces (called sub spaces). These sub spaces can have further sub spaces of their own. There is no limitation on the number of hierarchical levels. However, the space hierarchy is very important for all of the reasons specified above, in the previous section. Any business rules and security defined for a space is applicable to all of the content and sub spaces within that space. Your space hierarchy should look similar to the following screenshot: A space in Alfresco enables you to define various business rules, a dashboard view, properties, workflow, and security for the content belonging to each department. You can decentralize the management of your content by providing access to departments at the individual space levels. The example of the Intranet space should contain sub spaces, as shown in the preceding screenshot. You can create spaces by logging in as the administrator. It is also very important to set the security (by inviting groups of users to these spaces).
Read more
  • 0
  • 0
  • 2006

article-image-installing-drupal-themes
Packt
21 Oct 2009
5 min read
Save for later

Installing Drupal Themes

Packt
21 Oct 2009
5 min read
The large and active community of developers that has formed around Drupal guarantees a steady flow of themes for this popular CMS. The diversity of that community also assures that there will be a wide variety of themes produced. Add into the equation the existence of a growing number of commercial and open source web designs and you can be certain that somewhere out there is a design that is close to what you want. The issue becomes identifying the sources of themes and designs, and determining how much work you want to do yourself. You can find both design ideas and complete themes on the Web. You need to decide whether you want to work with an existing theme, or convert a design into a theme, or whether you want to start from scratch, unburdened by any preliminary constraints or alien code. For purposes of this article, we will be dealing with finding, installing, and then uninstalling an existing and current Drupal theme. This article assumes you have a working Drupal installation, and that you have access to the files on your server. Finding Additional Themes There are several factors to consider when determining the suitability of an existing theme. The first issue is compatibility. Due to changes made to Drupal in the 5.x series, older themes will not work properly with Drupal 5.x. Accordingly, your first step is to determine which version of Drupal you are running. To find the version information for your installation, go to Administer | Logs | Status Report. The first line of the Status Report tabular data will show your version number. If you do not see the Status Report option, then you are probably using a Drupal version earlier than 5.x. We suggest you upgrade as this book is for Drupal 5.x. If you know your Drupal version, you can confirm whether the theme you are considering is usable on your system. If the theme you are looking at doesn't provide versioning information, assume the worst and make sure you back up your site before you install the questionable theme. Once you're past the compatibility hurdle, your next concern is system requirements; does the theme require any additional extensions to work properly? Some themes are ready to run with no additional extensions required. Many themes require that your Drupal installation include a particular templating engine. The most commonly required templating engine is PHPTemplate. If you are running a recent instance of Drupal, you will find that the PHPTemplate engine is installed by default. You can also download a variety of other popular templating engines, including Smarty and PHPTal from http://drupal.org/project/Theme+engines.Check carefully whether the theme you've chosen requires you to download and install other extensions. If so, track down the additional extensions and install them first, before you install your theme. A good place to start looking for a complete Drupal theme is, perhaps not surprisingly, the official Drupal site. At Drupal.org, you can find a variety of downloads, including both themes and template engines. Go to http://drupal.org/project/Themes to find a listing of the current collection of themes. All the themes state very clearly the version compatibility and whether there are any prerequisites to run the theme. In addition to the resources on the official Drupal site, there is an assortment of fan sites providing themes. Some sites are open source, others commercial, and a fair number are running unusual licenses (most frequently asking that footers be left intact with links back to their sites). Some of the themes available are great; most are average. If your firm is brand sensitive, or your design idiosyncratic, you will probably find yourself working from scratch. Regardless of your particular needs, the theme repositories are a good place to start gathering ideas. Even if you cannot find exactly what you need, you sometimes find something with which you can work. An existing set of properly formed theme files can jump start your efforts and save you a ton of time. If you wish to use an existing theme, pay attention to the terms of usage. You can save yourself (or your clients) major headaches by catching any unusual licensing provisions early in the process. There's nothing worse than spending hours on a theme only to discover its use is somehow restricted. One source for designs with livable usage policies is the Open Source Web Design site, http://www.oswd.org, which includes a repository of designs, all governed by open source licensing terms. The down side of this resource is that all you get is the design—not the code, not a ready-made theme. You will need to convert the design into a usable theme. For this article, let's search out a completed theme and for the sake of simplicity, let's take one from the official Drupal site. I am going to download the Gagarin theme from Drupal.org. I'll refer to this theme as a working example of some ofthe steps below. You can either grab a copy of the same theme or you can use another—the principles are the same regardless. Gagarin is an elegant little theme from Garamond of the Russian Drupal community. Gagarin is set up for a two-column site (though it can be run in three columns) and works particularly well for a blog site.
Read more
  • 0
  • 0
  • 2836

article-image-creating-efficient-reports-visual-studio
Packt
21 Oct 2009
5 min read
Save for later

Creating efficient reports with Visual Studio

Packt
21 Oct 2009
5 min read
Report Services, Analysis Services, and Integration Services are the three pillars of Business Intelligence in Microsoft's vision that continues to evolve. Reporting is a basic activity, albeit one of the most important activities of an organization because it provides a specialized and customized view of the data of various forms (relational, text, xml etc) that live in data stores. The report is useful in making business decisions, scheduling business campaigns, or assessing the competition. The report itself may be required in hard copy in several document formats such as DOC, HTML, PDF, etc. Many times it is also required to be retrieved in an interactive form from the data store and viewed on a suitable interface, including a web browser. The Microsoft SQL Server 2005 Reporting Services, popularly known by its acronym SSRS, provides all that is necessary to create and manage reports and deploy them on a report server with output available in several document formats. The reader will greatly benefit from reading the several articles detailed in the author's Hodentek Blog. The content for the articles were developed using VS 2003, VS 2005, SQL 2000 and SQL 2005. (For more resources on Microsoft, see here.) The content for the present tutorial uses a Visual Studio 2008 Professional and a Microsoft SQL Server Compact 3.5 embeddable database for its data. In Visual Studio a Report Design Wizard guides you through fashioning a report from your choices. Create a Windows Project in VS2008 Create a new project from File | New | Project. Provide a name instead of the default name (WindowsApplicaiton1). This is changed to ReportDesign for this tutorial as shown in the next figure. VS 2008 supports multi-version targeting. In the top right of the New Project window you can see that this report is targeted for the NET 2.0 Framework Version and can be published to a Net 2.0 web site. Slightly enlarge the Form1. Drag and drop the Microsoft Report Viewer control shown in the next figure on to the form from the Toolbox. This has the same functionality as the ReportViewer control in VS 2005 as shown in the next figure. The control will be housed on the form as shown in the next figure. You can display the tasks needed to configure the Report Viewer by clicking on the Smart Task as shown in the same figure. The report will have all the functionalities like print, save to different formats, navigating through pages, etc. Working with the Report Wizard Now click on the Design a new report task. The opens the Report Wizard window as shown in the figure. Read the instructions on this page carefully. Click on the Next Button. This displays the Data Source Configuration Wizard shown in the next figure. Choosing a Data Source The application can obtain data from these different resources. Click on the Database icon and then click on the Next button. This displays the window where you need to select a connection to the data source. If there are existing connections you should be able to see them in the drop-down list box. Making a Connection to Get Data Click on the New Connection button. This brings up the Add Connection window showing a default connection to a Microsoft SQL Server Compact 3.5.NET Framework Data Provider. It also shows the location to be My Computer. This source can be changed by clicking on the Change... button. This will bring up the Change Data Source window where you can choose. As found in this version you have the following options: Microsoft SQL Server option lets you connect to SQL 2000 or 2005 using the .NET Framework Data Provider for SQL Server. Microsoft SQL Server Compact 3.5 lets you connect to a database file. Microsoft SQL Server Database File lets you connect to a Local Microsoft SQL Server Instance including a SQL Express. Although it is not explicitly stated what these versions are. For this tutorial the Compact 3.5 will be used (also uses a .NET Framework Data Provider of Compact 3.5). Click on the OK button in the Change Data Source window. VS 2008 installation also installs a database file on the computer for the SQL Server Compact 3.5. Click on Browse button (you could also create one if you like, herein it will be browsed). This brings up the Select SQL Server Compact 3.5 Database File window with the default location where the database file is parked as shown in the next figure. Click on the Northwind icon in the window and click on the Open button. This updates the Add Connection window with this information as shown in the next figure. You may test the connection by hitting the Test Connection button which should display a successful outcome as shown in the next figure. There is no need for a password as you are the owner. Click OK twice and this will take you back to the Data Source Configuration Wizard updating the connection information which you may review as shown in the next figure. Click on the Next button. This brings up the Microsoft Visual Studio message window giving you the option to bring this data source to your project.    
Read more
  • 0
  • 0
  • 6528
article-image-setting-most-popular-journal-articles-your-personalized-community-liferay-portal
Packt
21 Oct 2009
6 min read
Save for later

Setting up the most Popular Journal Articles in your Personalized Community in Liferay Portal

Packt
21 Oct 2009
6 min read
Personal community is a dynamic feature of Liferay portal. By default, the personal community is a portal-wide setting that will affect all of the users. It would be nice to have more features in the personal community such as showing the most popular journal articles. This article by Jonas Yuan will address how to set up the most popular journal articles in you personalized community and view the counter for other assets. In a web site, we will have a lot of journal articles (that is, web content) for a given article type. For example, for the article type Article Content, we will have articles talking about product family. We may want to know how many times the end users read each article. Meanwhile, it would be nice if we could show the most popular articles (for example, TOP 10 articles) for this given article type. As shown in the following screenshot, a journal article My EDI Product I is shown via a portlet Ext Web Content Display. Rating and comments on this article are also exhibited. At the same time, the medium-size image, polls, and related content of this article are listed, too. A view counter of this article is especially displayed under the ratings. Moreover, the most popular articles are exhibited with article title and number of views under related content. All these articles belong to the article type article-content. That is, the article in the current portlet Ext Web Content Display has the most popular articles only for the article type article-content. Of course, you can customize the portlet Web Content Display directly through changing JSP files. For demo purposes, we will implement the view counter in the portlet Ext Web Content Display. Meanwhile, we will implement the mostly popular articles via VM services and article templates. In addition, we will analyze the view counter for other assets such as Image Gallery images, Document Library documents, Wiki articles, Blog entries, Message Boards threads, and so on. Adding a view counter in the Web Content Display portlet First of all, let's add a view counter in the Ext Web Content Display portlet. As the function of view counter for assets (including journal articles) is provided in the model TagsAssetModel of the com.liferay.portlet.tags.model package in the /portal/portal-service/src folder, we could use this feature in this portlet directly. To do so, use the following steps: Create a folder journal_content in the folder /ext/ext-web/docroot/html/portlet/. Copy the JSP file view.jsp in the folder /portal/portal-web/docroot/html/portlet/ to the folder /ext/ext-web/docroot/html/portlet/journal_content and open it. Add the line <%@ page import="com.liferay.portlet.tags.model.TagsAsset" %> after the line <%@ include file="/html/portlet/journal_content/init.jsp" %>, and check the following lines: JournalArticleDisplay articleDisplay = (JournalArticleDisplay) request.getAttribute( WebKeys.JOURNAL_ARTICLE_DISPLAY); if (articleDisplay != null) { TagsAssetLocalServiceUtil.incrementViewCounter( JournalArticle.class.getName(), articleDisplay.getResourcePrimKey());} Then add the following lines after the line <c:if test="<%=enableComments %>"> and save it: <span class="view-count"> <% TagsAsset asset = TagsAssetLocalServiceUtil.getAsset (JournalArticle.class.getName(), articleDisplay.getResourcePrimKey());%> <c:choose> <c:when test="<%= asset.getViewCount() == 1 %>"> <%= asset.getViewCount() %> <liferay-ui:message key="view" />, </c:when> <c:when test="<%= asset.getViewCount() > 1 %>"> <%= asset.getViewCount() %> <liferay-ui:message key="views" />, </c:when> </c:choose></span> The code above shows a way to increase the view counter via the TagsAssetLocalServiceUtil.incrementViewCounter method. This method takes two parameters className and classPK as inputs. For the current journal article, the two parameters are JournalArticle.class.getName() and articleDisplay.getResourcePrimKey(). Then, this code shows a way to display view counted through the TagsAssetLocalServiceUtil.getAsset method. Similarly, this method also takes two parameters, className and classPK, as inputs. This approach would be useful for other assets, as the className parameter could be Image Gallery, Document Library, Wiki, Blogs, Message Boards, Bookmark, and so on. Setting up VM service We can set up the VM service to exhibit the most popular articles. We can also add the getMostPopularArticles method in the custom velocity tool ExtVelocityToolUtil. To do so, first add the following method in the ExtVelocityToolService interface: public List<TagsAsset> getMostPopularArticles(String companyId, String groupId, String type, int limit); And then add an implementation of the getMostPopularArticles method in the ExtVelocityToolServiceImpl class as follows: public List<TagsAsset> getMostPopularArticles(String companyId, String groupId, String type, int limit) { List<TagsAsset> results = Collections.synchronizedList(new ArrayList<TagsAsset>()); DynamicQuery dq0 = DynamicQueryFactoryUtil.forClass( JournalArticle.class, "journalarticle"). setProjection(ProjectionFactoryUtil.property ("resourcePrimKey")).add(PropertyFactoryUtil. forName("journalarticle.companyId"). eqProperty("tagsasset.companyId")). add(PropertyFactoryUtil.forName( "journalarticle.groupId").eqProperty( "tagsasset.groupId")).add(PropertyFactoryUtil. forName("journalarticle.type").eq( "article-content")); DynamicQuery query = DynamicQueryFactoryUtil.forClass( TagsAsset.class, "tagsasset") .add(PropertyFactoryUtil.forName( "tagsasset.classPK").in(dq0)) .addOrder(OrderFactoryUtil.desc( "tagsasset.viewCount")); try{ List<Object> assets = TagsAssetLocalServiceUtil. dynamicQuery(query); int index = 0; for (Object obj: assets) { TagsAsset asset = (TagsAsset) obj; results.add(asset); index ++; if(index == limit) break; } } catch (Exception e){ return results; } return results; } The preceding code shows a way to get the most popular articles by company ID, group ID, article type, and limited articles to be returned. DynamicQuery API allows us to leverage the existing mapping definitions through access to the Hibernate session. For example, DynamicQuery dq0 selects the journal articles by companyID, groupId, and type; DynamicQuery query selects tagsassets by classPK, which exists in DynamicQuery dq0; and tagsassets are ordered by viewCount as well. Finally, add the following method to register the above method in ExtVelocityToolUtil: public List<TagsAsset> getRelatedArticles(String companyId, String groupId, String articleId, int limit){ return _extVelocityToolService.getRelatedArticles(companyId, groupId, articleId, limit);} The code above shows a generic approach to get TOP 10 articles for any article types. Of course, you can extend this approach to find TOP 10 assets. This can include Image Gallery images, Document Library documents, Wiki articles, Blog entries, Message Boards threads, Bookmark entries, slideshow, videos, games, video queue, video list, playlist, and so on. You may practice these TOP 10 assets feature. Building article template for the most popular journal articles We have added view counter on journal articles. We have already built VM service for the most popular articles too. Now let's build an article template for them. Setting up the default article type As mentioned earlier, there is a set of types of journal articles, for example, announcements, blogs, general, news, press-release, updates, article-tout, article-content, and so on. In real case, only some of these types will require view counter, for example article-content. Let's configure the default article type for mostly popular articles. We can add the following line at the end of portal-ext.properties. ext.most_popular_articles.article_type=article-content The code above shows that the default article type for most_popular_articles is article-content.
Read more
  • 0
  • 0
  • 6716

article-image-managing-pages-liferay-portal-52-systems-development
Packt
21 Oct 2009
14 min read
Save for later

Managing Pages in Liferay Portal 5.2 Systems Development

Packt
21 Oct 2009
14 min read
Each site is represented as a community and each community is made up of a lot of pages, for example, public pages and private pages. In order to build web sites, we need to manage communities and, further, manage pages for each community. The Communities portlet provides the ability to create and manage communities and their users, as well as of the Manage Pages portlet. Extending Communities portlet The Communities portlet provides the ability to create and manage communities and their users. A community is a special group holding a number of users who share common interests. By default, a community is represented by the Group_ table with fields such as groupId, companyId, creatorUserId, name, description, type, typeSettings, friendlyURL, active, and so on. Now let's take an in-depth look at the customization of the community. As shown in the following screenshot, we may want to add one searchable field for each community, which is Keywords. For example, suppose we are creating a new community with the name Book Street, and the description a community for website www.bookpubstreet.com. Now we have a chance to add the new Keywords field with the value, for example, Book; Street; Palm Tree; Publication. Similarly, when editing the properties of a community—for example Name, Description, Type, and Active—we again have a chance to edit Keywords. In addition, we expect to have more fields in the customized communities: Created (when the community was created), ModifierUserId (who modified the community), and Modified (when the community was modified). As shown in the preceding screenshot, when listing communities, not only should the default fields (for example, Name, Type, Members, Online Now, Active, Pending Requests) and Actions icons (for example, Edit, Permissions, Manage Pages, Assign User Roles, Assign Members, Leave, and Delete) be displayed, but also the customized columns (for example, the username and Keywords) should be displayed. How do we implement these features? In this article, we're going to show how to customize the Communities portlet using the above requirements as examples. Obviously, it is open for you to customize this portlet in a number of ways according to your own requirements. In general, the processes for customization of this portlet should be the same. Building Ext Communities portlet The Communities portlet can be used to create and manage new portal communities and their users. As you can see, a community can be regarded as a separate portal instance; each community gets its own set of pages, content management system, shared calendar, and default permissions. Moreover, a user belonging to multiple communities can navigate among them within the same portal session. Generally speaking, we do not want to update the Communities portlet, but keep it as it is. Our goal is to customize and extend it. In general, this can be done by using the following two steps: Build a customized Ext Communities portlet, which has exactly the same functions, look, and feel as that of the original Communities portlet. Extend this customized portlet and let it have an additional model and service, and moreover, its own look and feel. In this part, let's build the Ext Communities portlet, having exactly same functions, look, and feel as that of the Communities portlet. Constructing the portlet Now let's define a Struts portlet with the name "Ext Communities". We first need to configure it in both portlet-ext.xml and liferay-portlet-ext.xml, and then set the title in Language-ext.properties, and then add the Ext Communities portlet to the Book category in liferay-display.xml. Locate the portlet-ext.xml file in the /ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines between </portlet> and </portlet-app> and save the file: <portlet> <portlet-name>extCommunities</portlet-name> <display-name>Ext Communities</display-name> <portlet-class>com.liferay.portlet.StrutsPortlet</portlet-class> <init-param><name>view-action</name> <value>/ext/communities/view</value></init-param> <expiration-cache>0</expiration-cache> <supports><mime-type>text/html</mime-type></supports> <resource-bundle> com.liferay.portlet.StrutsResourceBundle </resource-bundle> <security-role-ref> <role-name>power-user</role-name> </security-role-ref> <security-role-ref> <role-name>user</role-name> </security-role-ref> </portlet> As shown in the code above, the portlet-name element contains the canonical name of the portlet (for example, extCommunities). The display-name element contains a short name that is intended to be displayed in the portal (for example, Ext Communities). The portlet-class element contains the fully qualified class name of the portlet (for example, com.liferay.portlet.StrutsPortlet). The init-param element contains a name-value pair, for example view-action-ext/communities/view, as an initialization parameter of the portlet. Further, the expiration-cache defines expiration-based caching for this portlet. The supports element contains the supported MIME-type. The resource-bundle element contains a resource bundle class, for example com.liferay.portlet. StrutsResourceBundle. Finally, the security-role-ref element contains the declaration of a security role reference in the code of the web application. Secondly, let's register the extCommunities portlet in liferay-portlet-ext.xml as follows: Locate the liferay-portlet-ext.xml file in the /ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines immediately after <!-- Custom Portlets --> and save it: <portlet> <portlet-name>extCommunities</portlet-name> <struts-path>ext/communities</struts-path> <use-default-template>false</use-default-template> <restore-current-view>false</restore-current-view> </portlet> As shown in the code above, the Ext Communities portlet is registered in the portal. The portal will check struts-path to see whether a user has the required permissions to access the portlet or not. As you can see, struts-path has the value ext/communities. It means that all requests to the ext/communities/* path are considered a part of this portlet scope. Only those users whose request paths match ext/communities/* will be granted access. Moreover, the use-default-template element has the value false, so the portlet will not use any user's default template. The restore-current-view element has the value false so the portlet will reset the current view when toggling between maximized and normal states. Thirdly, add a title (for example, Ext Communities), for the Ext Communities portlet at Language-ext.properties as follows: Locate the Language-ext.properties file in the /ext/ext-impl/src/content folder and open it. Add the following line after javax.portlet.title.book_reports=Reports for Books and save it: javax.portlet.title.extCommunities=Ext Communities The code above provides mapping for the title of the portlet. If the mapping is not provided, the portal will show the default title javax.portlet.title.extCommunities. Finally, add the Ext Communities portlet to the Book category in liferay-display.xml as follows: Locate the liferay-display.xml file in the /ext/ext-web/docroot/WEBINF folder and open it. Add the following line immediately after the line <portlet id="book_reports" /> and save it: <portlet id="extCommunities" /> As shown in the code above, it adds the Ext Communities portlet to the category Book. From now on, you are able to select this portlet from the Book category directly when adding portlets to pages. Setting up actions Now, let's set up all actions required for the Ext Communities portlet. We need to prepare an action class, for example, ExtEditGroupAction. So how do we build this action? You can build the actions from scratch, but our purpose is to customize and extend the Communities portlet. In one word, we expect to reuse the out of the box portlet source code as much as possible and to write minimum code. As mentioned earlier, we have the portal project for the portal source code in the Eclipse IDE, which is referred to as the /portal prefix. We also have the ext project for customized code, which is referred to as the /ext prefix. The following is a process flow to build the ExtEditGroupAction action class of the Ext Communities portlet. Create a com.ext.portlet.communities.action package in the /ext/ext-impl/src folder. Create an ExtEditGroupAction class in this package and open it. Add the following lines and save it: public class ExtEditGroupAction extends EditGroupAction { public void processAction( ActionMapping mapping, ActionForm form, PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse) throws Exception { String cmd = ParamUtil.getString(actionRequest, Constants.CMD); try { if (cmd.equals(Constants.ADD) || cmd.equals(Constants.UPDATE)) { updateGroup(actionRequest); } else if (cmd.equals(Constants.DELETE)) { deleteGroup(actionRequest); } sendRedirect(actionRequest, actionResponse); } catch (Exception e) { if (e instanceof NoSuchGroupException || e instanceof PrincipalException) { SessionErrors.add(actionRequest, e.getClass().getName()); setForward(actionRequest, "portlet.ext.communities.error"); } else if (e instanceof DuplicateGroupException || e instanceof GroupFriendlyURLException || e instanceof GroupNameException || e instanceof RequiredGroupException) { SessionErrors.add(actionRequest, e.getClass().getName(), e); if (cmd.equals(Constants.DELETE)) { actionResponse.sendRedirect( ParamUtil.getString(actionRequest, "redirect")); } } else { throw e;} } } public ActionForward render(ActionMapping mapping, ActionForm form, PortletConfig portlonfig, RenderRequest renderRequest, RenderResponse renderResponse) throws Exception { try { ActionUtil.getGroup(renderRequest); } catch (Exception e) { if (e instanceof NoSuchGroupException || e instanceof PrincipalException) { SessionErrors.add(renderRequest, e.getClass().getName()); return mapping.findForward ("portlet.ext.communities.error"); } else {throw e;} } return mapping.findForward(getForward(renderRequest, "portlet.ext.communities.edit_community")); } } As shown in the code above, ExtEditGroupAction extends EditGroupAction from the com.liferay.portlet.communities.action package in the /portal/portal-impl/src folder. It overrides two methods (render and processAction) of EditGroupAction. Setting up page flow and page layout We have set up the action. We have also updated the forward path as the portlet.ext.communities.* value. In order to get the page flow working, we need to set up an action path and a page flow. First, let's set up the action path and page flow in struts-config.xml as follows: Locate the struts-config.xml file in the /ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines after <struts-config> <action-mappings> and save it: <!-- Ext Communities --> <action path="/ext/communities/edit_community" type="com.ext.portlet.communities.action. ExtEditGroupAction"> <forward name="portlet.ext.communities.edit_community" path="portlet.ext.communities.edit_community" /> <forward name="portlet.ext.communities.error" path="portlet.ext.communities.error" /> </action> <action path="/ext/communities/view" forward="portlet.ext.communities.view" /> The code above defines a set of action paths associated with the action and forward paths, as well as those mentioned earlier. For example, the action path /ext/communities/edit_community is associated with the com.ext.portlet.communities.action.ExtEditGroupAction action and the forward path names portlet.ext.communities.edit_community and portlet.ext.communities.error. Then based on the page flow and JSP files, let's define the page layout in tiles-defs.xml: Locate the tiles-defs.xml file in the ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines after <struts-config> <action-mappings> and save it: <!-- Ext Communities --> <action path="/ext/communities/edit_community" type="com.ext.portlet.communities.action. ExtEditGroupAction"> <forward name="portlet.ext.communities.edit_community" path="portlet.ext.communities.edit_community" /> <forward name="portlet.ext.communities.error" path="portlet.ext.communities.error" /> </action> <action path="/ext/communities/view" forward="portlet.ext.communities.view" /> The code above defines a set of action paths associated with the action and forward paths, as well as those mentioned earlier. For example, the action path /ext/communities/edit_community is associated with the com.ext.portlet.communities.action.ExtEditGroupAction action and the forward path names portlet.ext.communities.edit_community and portlet.ext.communities.error. Then based on the page flow and JSP files, let's define the page layout in tiles-defs.xml: Locate the tiles-defs.xml file in the ext/ext-web/docroot/WEB-INF folder and open it. Add the following lines after <tiles-definitions> and save it: <!-- Ext Communities --> <definition name="portlet.ext.communities" extends="portlet" /> <definition name="portlet.ext.communities.edit_community" extends="portlet.ext.communities"> <put name="portlet_content" value="/portlet/ext/communities/edit_community.jsp" /> </definition> <definition name="portlet.ext.communities.view" extends="portlet"> <put name="portlet_content" value="/portlet/ext/communities/view.jsp" /> </definition> <definition name="portlet.ext.communities.error" extends="portlet"> <put name="portlet_content" value="/portlet/communities/error.jsp" /> </definition> The code above defines the page layout for the Ext Communities portlet. For example, portlet.ext.communities.edit_community is associated with the JSP file /portlet/ext/communities/edit_community.jsp. In addition, it specifies that the community view page layout (for example, portlet.ext.communities.view) is associated with the JSP page file /portlet/ext/communities/view.jsp. Preparing JSP files We have now set up the actions. We have also set up page flow and page layout. Now let's set up the JSP files that are required for the Ext Communities portlet. We need to prepare JSP files such as view.jsp, edit_community.jsp, group_search.jsp, and so on. So how do we build this? You can build them from scratch. However, here we will copy and modify JSP files of the Communities portlet. In this section we expect to reuse the source code, including JSP files, as much as possible. First, let's create the view.jsp JSP file as follows: Create a communities folder within the /ext/ext-web/docroot/html/portlet/ext/ folder. Locate the view.jsp JSP file in the /portal/portal-web/docroot/html/portlet/communities folder, and copy it to the /ext/ext-web/docroot/html/portlet/ext/communities folder. Open view.jsp in the /ext/ext-web/docroot/html/portlet/ext/communities folder, update /communities/edit_community with /ext/communities/edit_community as shown in the following two lines, and save it: portletURL.setParameter("struts_action", "/ext/communities/view"); <liferay-ui:search-form page="/html/portlet/ext/communities/group_search.jsp" searchContainer="<%= searchContainer %>" showAddButton="<%= showTabs1 %>" /> Next, we need to create the JSP file edit_community.jsp as follows: Locate the JSP file edit_community.jsp in the /portal/portal-web/docroot/html/portlet/communities folder, and copy it to the /ext/extweb/docroot/html/portlet/ext/communities folder. Open edit_community.jsp in the /ext/ext-web/docroot/html/portlet/ext/communities folder, update /communities/edit_community with /ext/communities/edit_community as shown in following line, and save it: <form action="<portlet:actionURL windowState="<%= WindowState.MAXIMIZED. toString() %>"> <portlet:param name="struts_action" value="/ext/communities/edit_community" /> </portlet:actionURL>" method="post" name="<portlet:namespace />fm" onSubmit="<portlet:namespace />saveGroup(); return false;"> In addition, we need to make the button Add Community available, in the following manner: Locate JSP file group_search.jsp in the /portal/portal-web/docroot/html/portlet/enterprise_admin folder. Copy the JSP file group_search.jsp from /portal/portal-web/docroot/html/portlet/enterprise_admin to /ext/ext-web/docroot/html/portlet/ext/communities, and open it. Update /communities/edit_community with /ext/communities/edit_community as shown in the following lines, and save it: submitForm(document.<portlet:namespace />fm, '<portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>"> <portlet:param name="struts_action" value="/ext/communities/edit_community" /> <portlet:param name="redirect" value="<%= currentURL %>" /> </portlet:renderURL>'); Congratulations! You have cloned the Communities portlet. Finally, we can deploy updates into Tomcat as follows: Stop Tomcat if it is running. Click on the Ant target: deploy at the Ant view ext. Start Tomcat. Open up a new browser with the URL http://localhost:8080. Click on Sign in and enter test@liferay.com / test. Click on Add Application | Book
Read more
  • 0
  • 0
  • 1738
Modal Close icon
Modal Close icon