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
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Web Design

132 Articles
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
  • 1346

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
  • 1262

article-image-working-drupal-audio-flash-part-1
Packt
20 Oct 2009
7 min read
Save for later

Working with Drupal Audio in Flash (part 1)

Packt
20 Oct 2009
7 min read
Within the past five years, there has been a major change in the type of content found on the World Wide Web. In just a few short years, content has evolved from being primarily text and images, into a multimedia experience! Drupal contributors have put much effort in making this integration with multimedia as easy as possible. However, one issue still remains: in order to present multimedia to your users, you cannot rely on Drupal alone. You must have another application layer to present that media. This is most typically a Flash application that allows the user to listen or watch that media from within their web browser. This article explores how to use Drupal to manage a list of audio nodes and also builds a Flash application to play that music. When it comes to multimedia, Flash is the portal of choice for playing audio on a web sites. Integrating audio in Drupal is surprisingly easy, thanks to the contribution of the Audio module. This module allows you to upload audio tracks to your Drupal website (typically in MP3 format), by creating an Audio node. It also comes with a very basic audio player that will play those audio tracks in the node that was created. To start, let's download and enable the Audio module along with the Token, Views, and getID3 modules, which are required for the Audio module. The modules that you will need to download and install are as follows: Audio—http://www.drupal.org/project/audio Views—http://www.drupal.org/project/views Token—http://www.drupal.org/project/token getID3—http://www.drupal.org/project/getid3 At the time of writing this article, the Audio module was still considered "unstable". Because of this, I would recommend downloading the development version until a stable release has been made. It is also recommended to use the development or "unstable" versions for testing purposes only. Once we have downloaded these modules and placed them in our site's modules folder, we can enable the Audio module by first navigating to the Administer | Modules section, and then enabling the checkboxes in the Audio group as follows: After you have enabled these modules, you will probably notice an error at the top of the Administrator section that says the following: This error is shown because we have not yet installed the necessary PHP library to extract the ID3 information from our audio files. The ID3 information is the track information that is embedded within each audio file, and can save us a lot of time from having to manually provide that information when attaching each audio file to our Audio nodes. So, our next step will be to install the getID3 library so that we can utilize this great feature. Installing the getID3 library The getID3 library is a very useful PHP library that will automatically extract audio information (called ID3) from any given audio track. We can install this useful utility by going to http://sourceforge.net/project/showfiles.php?group_id=55859, which is the getID3 library URL at SourceForge.net. Once we have done this, we should see the following: We can download this library by clicking on the Download link on the first row, which is the main release. This will then take us to a new page, where we can download the ZIP package for the latest release. We can download this package by clicking on the latest ZIP link, which at the time of writing this article was getid3-1.7.9.zip Once this package has finished downloading, we then need to make sure that we place the extracted library on the server where the getID3 module can use it. The default location for the getID3 module, for this library, is within our site's modules/getid3 directory. Within this directory, we will need to create another directory called getid3, and then place the getid3 directory from the downloaded package into this directory. To verify that we have installed the library correctly, we should have the getid3.php at the following location: Our next task is to remove the demos folder from within the getid3 library, so that we do not present any unnecessary security holes in our system. Once this library is in the correct spot, and the demos folder has been removed, we can refresh our Drupal Administrator section and see that the error has disappeared. If it hasn't, then verify that your getID3 library is in the correct location and try again. Now that we have the getID3 library installed, we are ready to set up the Audio content type. Setting up the Audio content type When we installed the Audio module, it automatically created an Audio content type that we can now use to add audio to our Drupal web site. But before we add any audio to our web site, let's take a few minutes to set up the Audio content type to the way we want it. We will do so by navigating to Administer | Content Types, and then clicking on the edit link, next to the Audio content type. Our goal here is to set up the Audio content type so that the default fields make sense to the Audio content type. Drupal adds the Body field to all new content types, which doesn't make much sense when creating an Audio content. We can easily change this by simply expanding the Submission form settings. We can then replace the Body label with Description, since it is easily understood when adding new Audio tracks to our system. We will save this content type by clicking on the Save content type button at the bottom of the page. Now, we are ready to start adding audio content to our Drupal web site. Creating an Audio node We will add audio content by going to Create Content, and then clicking on Audio, where we should then see the following on the page: You will probably notice that the Title of this form has already been filled out with some strange looking text (as shown in the previous screenshot). This text is a series of tags, which are used to represent track information that is extracted using the getID3 module that we installed earlier. Once this ID3 information is extracted, these tags will be replaced with the Title and Artist of that track, and then combined to form the title of this node. This will save a lot of time because we do not have to manually provide this information when submitting a new audio track to our site. We can now upload any audio track by clicking on the Browse button next to the Add a new audio file field. After it adds the file to the field, we can submit this audio track to Drupal by clicking on the Save button at the bottom of the page, which will then show you something like the following screenshot: After this node has been added, you will notice that there is a player already provided to play the audio track. Although this player is really cool, there are some key differences between the player provided by the Audio module and the player that we will create later in this article. How our player will be different (and better) The main difference between the player that is provided by the Audio module and the player that we are getting ready to build is how it determines which file to play. In the default player, it uses flash variables passed to the player to determine which file to play. This type of player-web site interaction places the burden on Drupal to provide the file that needs to be played. In a way, the default player is passive, where it does nothing unless someone tells it to do something. The player that we will be building is different because instead of Drupal telling our player what to play, we will take an active approach and query Drupal for the file we wish to play. This has several benefits, such as that the file path does not have to be exposed to the public in order for it to be played. So, let's create our custom player!
Read more
  • 0
  • 0
  • 1861
Banner background image
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 $15.99/month. Cancel anytime
article-image-building-web-service-driven-application-flash-drupal
Packt
20 Oct 2009
6 min read
Save for later

Building a Web Service-driven Application with Flash in Drupal

Packt
20 Oct 2009
6 min read
So, let's take a step-by-step approach on how to accomplish this on the Flash side, which as far as I am concerned, is the fun side! Click here to access all the codes used in this article. Step 1: Creating our Flash application With our chapter2 project open, we can shift our focus to the Actions panel within the Flash IDE. Although working with the Actions panel is great for small applications, we will eventually build onto this Flash application, which might make it impractical to keep all of our ActionScript code within the Actions panel. Because of this, we will first need to create a separate ActionScript file that will serve as our main entry point for our Flash application. This will allow us to easily expand our application and add to the functionality without modifying the Actions panel for every addition we make. Step 2: Creating a main.as ActionScript file For this step, we will simply create an empty file next to our chapter2.fla file called main.as. After you have created this new file, we will then need to reference it within our Actions panel. To do this, we will use the include keyword in ActionScript to include this file as the main entry point for our application. So, shifting our focus back to the chapter2.fla file, we will then place the following code within the Actions panel: include "main.as";stop(); Now that we are referencing the main.as file for any of the ActionScript functionality, we will no longer need to worry about the Actions panel and add any new functionality directly to the main.as file. Now, for the following sections, we will use this main.as file to place all of our ActionScript code that will connect and extract information from our Drupal system, and then populate that information in a TextField that we will create later. So, let's jump right in and write some code that connects us with our Drupal system. Step 3: Connecting to Drupal For this step, we will first need to open up our empty main.as file so that we can add custom functionality to our Flash application. With this file open in our Flash IDE, our first task will be to connect with Drupal. Connecting to Drupal will require us to make a remote call to our Drupal installation, and then handle its response correctly. This will require the use of asynchronous programming techniques along with some standard remoting classes built into the ActionScript 3 library. I will spend some time here discussing the class used by ActionScript 3 to achieve remote communication. This class is called NetConnection. Using the NetConnection class The NetConnection class in ActionScript 3 is specifically used to achieve remote procedure calls within a Flash application. Luckily, this class is pretty straight forward and does not have a huge learning curve on understanding how to utilize it for communicating with Drupal. Using this class requires that we first create an instance of this class as an object, and then initialize that object with the proper settings for our communication. But let's tackle the creation first, which will look something like this in our main.as file: // Declare our Drupal connectionvar drupal:NetConnection = new NetConnection(); Now, you probably noticed that I decided to name my instance of this net connection drupal. The reason for this is to make it very clear that any place in our Flash application where we would like to interact with Drupal, we will do so by simply using our drupal NetConnection object. But before we use this connection, we must first specify what type of connection we will be using. In any NetConnection object, we can do this by providing a value for the variable objectEncoding . This variable lets the connection know how to structure the XML format when communicating back and forth between Flash and Drupal. Currently, there are only two types of encoding to choose from: AMF0 or AMF3. AMF0 is used for ActionScript versions less than 3, while AMF3 is used for ActionScript 3. ActionScript 1 and 2 are much less efficient than version 3, so it is highly recommended to use ActionScript 3 over 1 or 2. Since we are using ActionScript 3, we will need to use the AMF3 format, and we can provide this as follows: // Declare our Drupal connectionvar drupal:NetConnection = new NetConnection();drupal.objectEncoding = ObjectEncoding.AMF3; Now that we have an instance ready to go, our first task will be to connect to the Drupal gateway that we set up in the previous section. Connecting to a remote gateway Connecting to a remote gateway can be performed using the connect command on our drupal NetConnection object. But in order for us to connect, we must first determine the correct gateway URL to pass to this function. We can find this by going back to our Drupal installation and navigating to Administer | Services. In the Browse section, you will see a link to the servers available for remote procedure calls as shown in the following screenshot: For every listed server, we can click on each link to verify that the server is ready for communication. Let's do this by clicking on the link for AMFPHP, which should then bring up a page to let us know that our AMFPHP gateway is installed properly. We can also use this page to determine our AMFPHP gateway location, since it is the URL of this page. By observing the path of this page, we can add our AMFPHP server to our main.as file by combining the base URL of our site and then adding the AMFPHP services gateway to that base. // Declare our baseURL and gateway string.var baseURL:String = "http://localhost/drupal6";var gateway:String = baseURL + "/services/amfphp";// Declare our Drupal connectionvar drupal:NetConnection = new NetConnection();drupal.objectEncoding = ObjectEncoding.AMF3;// Connect to the Drupal gatewaydrupal.connect( gateway ); It is important to note that the connect routine is synchronous, which means that once this function is called, we can immediately start using that connection. However, any remote procedure call that we make afterwards, will be asynchronous, and will need to be handled as such. The function that can be used to make these remote procedure calls to Drupal is called call.
Read more
  • 0
  • 0
  • 1671

article-image-date-and-calendar-module-drupal-5-part-1
Packt
20 Oct 2009
5 min read
Save for later

Date and Calendar Module in Drupal 5: Part 1

Packt
20 Oct 2009
5 min read
Recipe 33: Understanding Date formats Drupal dates are typically stored in one of two ways. Core Drupal dates—including Node: Created Time, and Node: Updated Time—are stored as Unix timestamps. Contributed module date fields can be stored as either a timestamp or a format known as ISO. Neither style is particularly friendly to human readers, so both field types are usually formatted before users see them. This recipe offers a tour of places in Drupal where dates can be formatted and information on how to customize the formats. What's that Lucky Day?The Unix timestamp 1234567890 fell on Friday the 13th, in February, 2009. This timestamp marks 1,234,567,890 seconds since January 1, 1970. The same date/time combination would be stored in a date field in ISO format as 2009-02-13T23:31:30+00:0. ISO is an abbreviation for the International Organization for Standardization Opening the browser windows side-by-side will help you understand date formatting. In the left window, open YOURSITE.com/admin/settings/ date-time to view the settings page for date and time. In the right window, open the API page of code that defines these system date time settings at http://api.drupal.org/api/function/system_date_time_settings/5. Compare each item in the $datemedium array, for instance, with the associated Medium date format drop-down. a – am/pm D – Day, Mon through Sun d – Date, 01 to 31 (with leading zeroes) F – Month, January through December (mnemonic, F = Full name) g – Hours, 1 through 12 H – Hours, 00 through 23 i – Minutes, 00 to 59 j – Date, 1 to 31 (No leading zeroes) l – Sunday through Saturday m – Month, 01 through 12 M – Month, Jan through Dec s – Seconds, 00 through 59 (with leading zeroes) S – Month Suffix, st, nd, rd, or th. Works well with j Y – Year, Examples: 1999 or 2011 Below is the list of codes for many commonly used date and time formats. A more comprehensive list appears at http://us.php.net/date. Explore Drupal places where these codes may be used. The first four locations in the table below are available in the Drupal administrative interface. The last three involve editing files on the server—these edits are completely optional. Location Details CCK field setup Custom Input formats admin/content/types/story/add_field   After the field widget is specified admin/content/types/<CONTENTTYPE>/fields/field_<FIELDNAME> Near the top of the page.   Near the bottom of the page:   Formatting Fields in Views. admin/build/views/<VIEW_NAME>/edit CCK Date fields are set via the Options drop-down in the Fields fieldset.   Custom date formats for core fields, such as Node: Created Time are set via handler and options from elements.   Default Date and Time settings admin/settings/date-time Set the default time zone, Short, Medium, and Long date formats, and the first day of the week.   Post Settings This may be one of the harder-to-find settings in Drupal, enabling the Post settings to be turned-off for specified content types. (An example of a post setting would be: Submitted by admin on Sun, 10/12/2008 - 4:55pm. The setting is found on the right-hand side of this URL: admin/build/themes/settings Use the following mouse click trail to get to this URL: Administer | Site Building | Themes | Configure (Click on the Configure tab at the top of the page. If you click on the Configure link in the Operations column, you will still need to click the Configure tab at the top to get to the global settings.)   Variable overrides in settings.php You may override variables at the bottom of the /sites/default/settings.php file. Remove the appropriate pound signs to enable the $conf array, and add a setting as shown below. Note that this is a quick way to modify the post settings format, which draws from the medium date variable. $conf = array( #   'site_name' => 'My Drupal site', #   'theme_default' => 'minnelli', #   'anonymous' => 'Visitor', 'date_format_medium' => 'l F d, Y'  ); *.tpl.php files Examples: node-story.tpl.php <?php print format_date($node->created, 'custom', 'F Y'); ?> comment.tpl.php <?php echo t('On ') . format_date($comment->timestamp,   'custom'  , 'F jS, Y'); ?> <?php echo theme('username',   $comment) . t(' says:'); ?> template.php Redefine $variables['submitted'] Example from blommor01 theme:   $vars['submitted'] =  t('!user - <abbr class="created"   title="!microdate">!date</abbr>', array(    '!user' => theme('username', $vars['node']),    '!date' => format_date($vars['node']->created),    '!microdate' => format_date($vars['node']->   created,'custom', "Y-m-dTH:i:sO")   )); Recipe notes Note that when using the PHP date codes, additional characters may be added, including commas, spaces, and letters. In the template.php example, a backslash was used to show that the letter 'T' will be printed, rather than the formatted return values. Below are more examples of added characters: F j, Y, g:i a // August 27, 2010, 5:16 pmm.d.y // 08.27.10 You may occasionally find that an online date converter comes in handy. http://www.timestampconverterer.com/ (this URL includes the word "converter" followed by another "er"). http://www.coryking.com/date-converter.php
Read more
  • 0
  • 0
  • 1966

article-image-navigating-online-drupal-community
Packt
16 Oct 2009
9 min read
Save for later

Navigating the Online Drupal Community

Packt
16 Oct 2009
9 min read
Recipe 87: Creating an issue Page Bookmark IngredientsWeb Browser The issue queue is the central place of progress for Drupal modules. It serves as a place to find answers, patches, new ideas, and work on common concerns. Issues are referenced by number. On occasion, a web page will contain an issue queue number in text form rather than a full link to the issue. This recipe, once set up, simply saves the trouble of having to type drupal.org/node/ into the browser address bar. Just select the number and the bookmark will take you there. In Firefox add a new Bookmark onto the toolbar. Select Bookmarks | Organize Bookmarks | Bookmarks Toolbar | Organize | New Bookmark Set the Name to Drupal Issue and set the Location to the following: javascript:inum=escape(getSelection());location.href='http://www.drupal.org/node/'+inum. Visit a web page that contains an issue number and select the issue number text. For instance, try http://cvs.drupal.org/viewvc.py/drupal/contributions/modules/views/modules/views_taxonomy.inc. (Be sure to exclude the surrounding space and pound sign when selecting the number.) Click the Drupal Issue button in the bookmark toolbar. Recipe notes This bookmark approach may be replicated to visit a URL containing any selectable text. For instance, below is a variation to display all of your delicious bookmarks tagged with the selected text. (Delicious.com—also found at http://del.icio.us, is a wonderful online bookmark service.) Replace <ACCOUNTNAME> with your delicious.com account. Name: DeliciousLocation: javascript_tag=escape(getSelection());location.href='http://delicious.com/<ACCOUNTNAME>/'+tag Recipe 88: Searching the Views issue queue IngredientsWeb Browser In this recipe we look closely at how to search the Views issue queue. The lessons apply to all other Drupal projects as well. It is always a good idea to search the issue queue for related content before posting. Log on to drupal.org (if you are not already a member of the Drupal site, become a member). Basic Search Visit http://drupal.org/project/issues/views. At this main issue queue page you may search for text or filter by Status, Priority, Category, Version, or Component. These options are discussed in further detail below. You may also sort the table of issues by clicking on the table header. By default, the table is sorted by date. Advanced Search Go to the Views issue queue Advanced Search page. Visit the URL directly, at http://drupal.org/project/issues/search/views. From the project page (drupal.org/project/views), find the Issues block on the left, and click on the Advanced Search link. From the issue queue (drupal.org/project/issues/views), the Advanced Search Link appears under the title. There are a variety of routes to get there: Get to know the search options. Although there are ten form elements to choose, most users will routinely use just a few, leaving the other options blank. Search For (Routinely used): Enter search text. Use quotation marks to create a phrase. Assigned: This field is generally used by issue maintainers. Submitted by: This is most often used to find your own issues, though it could be used to see what other Drupal users are posting as well. Participant: This is also used to find your own posts. Note that Submitted by finds only the initial post by a user in the issue queue. Participant additionally includes responses to initial posts. Status: Leave blank to get all statuses. You may also select multiple options. For instance, you could select all issues designated as needs work, needs review, and reviewed & tested by the community. Scroll down the list and note Status filters such as closed issues, duplicates, issues that the maintainer won't fix, and features noted as by design. These are the statuses that are excluded if you select -Open Issues-. Priority: Leave blank to get all priorities. Category: Leave blank to get all categories. Version (Routinely used): A relative new option, 5.x issues saves you the trouble of having to Shift+click on each Drupal 5 release name. Component: The views module issue queue offers more component options than most modules. As a result, users may not always be familiar with properly assigning a component when they create an issue. A search of exposed filters components, for instance, may not find as many results as a text search of "exposed filters." Component can occasionally be a helpful selection, but is most often left blank. Issue Tags: These may be a challenge to search since few people add tag issues. This may become a more popular option in the future. Recipe notes Search ideas: Find all your posts by filling in your drupal.org user name under participant. Find patches by selecting all of the four patch statuses. Find all documentation issues connected to Views for Drupal 5.x. Go to another issue queue http://drupal.org/project/issues/search/<MODULENAME> and search for the word Views. From the module issue pages http://drupal.org/project/issues/<MODULENAME> you may also review module Statistics, and Subscribe to issues. Subscribe to your Own Issues (the default), None, or All Issues. I don't recommend the latter for the Views module as you will be setting yourself up for a deluge of email. Search across all projects at http://drupal.org/search/issues. Recipe 89: Posting an issue Posting a New issue If you are new to posting Drupal issues, consider just reading the issue queue for at least several days before posting. This will help you to get a sense of the culture of issue queue interaction. If you don't already have an account on drupal.org get one. Look for the User login block on the home page, and click on Create new account. Complete the steps to login. Search the issue queue before you post! (Recipe 88). If your topic already has an associated active issue, reply rather than posting a new issue.Also, before posting to the issue queue in a panic read the Drupal Troubleshooting FAQ http://drupal.org/Troubleshooting-FAQ. For instance, standard fare is to increase memory in the face of the White Screen of Death (WSOD) or to disable buggy modules by setting the status = 0 in the system table. Be sure to know which version of the module you're using. Is it the dev (development) version? Is it the latest recommended release? The version number can generally be found at http://YOURSITE.com/admin/build/modules. To start a new issue, go to http://drupal.org/project/issues/<MODULENAME> and click on Create a new issue. This directs the browser to: http://drupal.org/node/add/project-issue/<MODULENAME>. For the Views module, the link at http://drupal.org/node/add/project-issue/views offers guidance (in bold!) for posting. Read it! Much of it applies to Views 2 but it contains useful information for Views 1 users as well. Required fields for a new issue include Version, Component, Category, Title, and Description. Be thoughtful with these details. For instance, do not title your issue HELP??!! A much more useful description would be something like Missing taxonomy terms in filters. Priority should generally be left as normal. Critical is reserved for occasions then the module simply does not work. Responding to an existing issue You may also respond to an existing issue by selecting the Add New Comment link or one of the Reply links on an individual issue page. Another option is just to scroll down to the bottom of the issue page, and begin entering a response. Unlike some forum tools, in which replies are indented, all new comments are given a new comment number, and added to the bottom of the comments. When responding to an issue you may take a variety of actions: Change the Issue Title. In general, don't change this unless you have a very good reason (for instance, if the original title is misleading, or spelled wrong). Some people are used to forums where a response can have a different name as the original post. In the issue queue, changing the name when responding to an issue actually changes the name of the issue. This is generally best left untouched. Change the Project. A question that someone asks in the Views issue queue may be more appropriately managed in the issue queue for a different module. This is a rare change generally left to the maintainer of one of the two modules who will know in which issue queue a discussion belongs. Change the Version number, Component, Category, or Priority. These changes are rare (correcting the version number is probably the most common). When changes are made, they are noted in the post as shown below: Change Assign. Do not assign someone other than yourself to an issue. Assign yourself if you are sure that you will soon fix the issue. It is quite common to leave this as Unassigned. Change the Status. For instance: Mark an issue as a duplicate (always provide a pointer to the issue it duplicates). Note that a patch is reviewed and tested by the community. Post a question, patch, answer, or idea related to the issue in the Comment section. Open the Input format fieldset below the comment field to see what markup is available. Note the <code> tag, for instance (and remember to close it with a </code> tag). Attach a file. Recipe notes Remember that respondents and maintainers are volunteers. They are generally very busy people who want to help, but they do not have time to do free consulting. See the following pages for spirited discussions about issue queue etiquette: http://acko.net/blog/whats-wrong-with-drupal http://paul.leafish.co.uk/articles/drupal/on_subscribing_to_module_portingupdating_issues One discussion theme is the merit of simply sending the word subscribe to the issue queue. People sometimes do this so that they can track an issue—receiving an email alert each time something new is posted. On drupal.org it is possible to subscribe to a node only if you leave a comment, but most people prefer comments with substance. You may create functionality similar to the Drupal issue queue on your own site by installing the project, project_issue, and comment_upload modules.
Read more
  • 0
  • 0
  • 2529
article-image-working-drupal-audio-flash-part-2
Packt
16 Oct 2009
6 min read
Save for later

Working with Drupal Audio in Flash (part 2)

Packt
16 Oct 2009
6 min read
Although there are a handful of controls that we can add to this custom audio player, this section will demonstrate the concept by adding the most basic control for multimedia, which is the play and pause buttons. Adding a play and pause button To begin, we will need to first move and resize our title field within our Flash application, so that it can hold more text than "Hello World". We can then make room for some new controls that will be used to control the playback of our audio file. Again, the design of each of these components is subjective, but what is important is the MovieClip instance hierarchy, which will be used within our ActionScript code. Before we begin, we will need to create a new layer in our TIMELINE that will be used to place all AudioPlayer objects. We will call this new layer player: Creating a base button MovieClip Our base button will simply be a rounded rectangle, which we will then add some gradients to, so as to give it depth. We can do this by first creating a rounded rectangle with a vertical linear gradient fill as follows: We can now give it some very cool depth by adding a smaller rounded rectangle within this one, and then orient the same gradient horizontally. An easy way to do this is to copy the original shape and paste it as a new shape. Once we have a new copy of our original rounded rectangle, we can navigate to Modify | Shape | Expand fill, where we will then select Inset, change our Distance to 4px, and then click on OK. After doing this, you will realize how such a simple contrast in gradients can really bring out the shape. After we have our new button shape, we will then need to create a new MovieClip, so that we can reuse this button for both the play and pause buttons. To do this, simply select both the rounded rectangle regions, and then choose Modify | Convert to Symbol in the Flash menu. We are going to call this new movie clip mcButton. Now that we have a base button MovieClip, we can now add the play and pause symbols to complete the play and pause buttons. Adding the PlayButton movie clip The first button that we will create is the play button, which simply consists of a sideways triangle (icon) with the button behind it. To do this, we will first create a new movie clip that will hold the button we just created, and the play icon. We can do this by first clicking on the mcButton movie clip, and then creating a new movie clip from that by selecting Modify | Convert to Symbol. We will call our new movie clip mcPlayButton. What we are really doing here is creating a parent movie clip for our mcButton, which will allow us to add new specific elements. For the play button, we simply want to add a play symbol. To do this, we first want to make sure that we are within the mcPlayButton movie clip by double-clicking on this symbol, so that our breadcrumb at the top of the stage looks as follows: Our next task is to modify our timeline within this movie clip so that we can separate the icon from the button. We can do this by creating two new layers within our timeline, called button (which will hold our button) and icon (which we will create in the next section). We are now ready to start drawing the play icon. Drawing a play icon To draw a Play icon, we will need to first select the PolyStar Tool by clicking and holding on the tool until you can select the PolyStar Tool. This tool will allow us to create a triangle, which we will use for the play icon in our play button. But before we can start drawing, we need to first set up the PolyStar Tool so that it will draw a triangle. We can do this by clicking on the Options button within the Properties tab, which will then bring up a dialog, where we can tell it to draw a polygon with three sides (triangle). After we click on OK, we will then need to change the fill color of this triangle, so that it is visible on our button. We will just change the fill color to Black. We can then move our cursor onto the stage where the button is, and then draw our triangle in the shape of a play button icon. Remember, if you do not like the shape of what you made, you can always tweak it using the transform tool. When we are done, we should have something that resembles a play button! Our next task is to create a pause button. Since we have already created the play button, which is similar to the pause button except for the icon, we can use a handy tool in Flash that will let us duplicate our play button, and then modify our duplication for the pause button icon. Creating a pause button from the play button In order to create our pause button, we will first need to duplicate our play button into a new movie clip, where we can change the icon from play to pause. To do this, we will first direct our attention to the library section of our Flash IDE, which should show us all of the movie clips that we have created so far. We can find the LIBRARY by clicking on the button on the right-hand side of our workspace. To create a duplicate, we will now right-click on the mcPlayButton movie clip, and then select the option Duplicate. This will then bring up a dialog very similar to the dialog when we created new symbols, but this time, we are defining a new movie clip name that will serve as a duplicate for the original one. We will call our new movie clip duplicate mcPauseButton. Now that we have created our duplicate movie clip, the next task is to change the icon within the pause button. We can do this by opening up our mcPauseButton movie clip by double-clicking on that name within the Library. At this point, we can now change the icon of our pause button without running any risk of also modifying the play button (since we created a duplicate). When we are done, we should have a complete pause button. We now have play a nd pause buttons that we will use to link to our AudioPlayer class.
Read more
  • 0
  • 0
  • 2167

article-image-how-bridge-client-server-gap-using-ajax-part-ii
Packt
15 Oct 2009
7 min read
Save for later

How to Bridge the Client-Server Gap using AJAX (Part II)

Packt
15 Oct 2009
7 min read
AJAX and events Suppose we wanted to allow each dictionary term name to control the display of the definition that follows; clicking on the term name would show or hide the associated definition. With the techniques we have seen so far, this should be pretty straightforward: $(document).ready(function() { $('.term').click(function() { $(this).siblings('.definition').slideToggle(); });}); When a term is clicked, this code finds siblings of the element that have a class of definition, and slides them up or down as appropriate. All seems in order, but a click does nothing with this code. Unfortunately, the terms have not yet been added to the document when we attach the click handlers. Even if we managed to attach click handlers to these items, once we clicked on a different letter the handlers would no longer be attached. This is a common problem with areas of a page populated by AJAX. A popular solution is to rebind handlers each time the page area is refreshed. This can be cumbersome, however, as the event binding code needs to be called each time anything causes the DOM structure of the page to change. We can implement event delegation, actually binding the event to an ancestor element that never changes. In this case, we'll attach the click handler to the document using .live() and catch our clicks that way: $(document).ready(function() { $('.term').live('click', function() { $(this).siblings('.definition').slideToggle(); });}); The .live() method tells the browser to observe all clicks anywhere on the page. If (and only if) the clicked element matches the .term selector, then the handler is executed. Now the toggling behavior will take place on any term, even if it is added by a later AJAX transaction. Security limitations For all its utility in crafting dynamic web applications, XMLHttpRequest (the underlying browser technology behind jQuery's AJAX implementation) is subject to strict boundaries. To prevent various cross-site scripting attacks, it is not generally possible to request a document from a server other than the one that hosts the original page. This is generally a positive situation. For example, some cite the implementation of JSON parsing by using eval() as insecure. If malicious code is present in the data file, it could be run by the eval() call. However, since the data file must reside on the same server as the web page itself, the ability to inject code in the data file is largely equivalent to the ability to inject code in the page directly. This means that, for the case of loading trusted JSON files, eval() is not a significant security concern. There are many cases, though, in which it would be beneficial to load data from a third-party source. There are several ways to work around the security limitations and allow this to happen. One method is to rely on the server to load the remote data, and then provide it when requested by the client. This is a very powerful approach as the server can perform pre-processing on the data as needed. For example, we could load XML files containing RSS news feeds from several sources, aggregate them into a single feed on the server, and publish this new file for the client when it is requested. To load data from a remote location without server involvement, we have to get a bit sneakier. A popular approach for the case of loading foreign JavaScript files is injecting <script> tags on demand. Since jQuery can help us insert new DOM elements, it is simple to do this: $(document.createElement('script')) .attr('src', 'http://example.com/example.js') .appendTo('head'); In fact, the $.getScript() method will automatically adapt to this technique if it detects a remote host in its URL argument, so even this is handled for us. The browser will execute the loaded script, but there is no mechanism to retrieve results from the script. For this reason, the technique requires cooperation from the remote host. The loaded script must take some action, such as setting a global variable that has an effect on the local environment. Services that publish scripts that are executable in this way will also provide an API with which to interact with the remote script. Another option is to use the <iframe> HTML tag to load remote data. This element allows any URL to be used as the source for its data fetching, even if it does not match the host page's server. The data can be loaded and easily displayed on the current page. Manipulating the data, however, typically requires the same cooperation needed for the <script> tag approach; scripts inside the <iframe> need to explicitly provide the data to objects in the parent document. Using JSONP for remote data The idea of using <script> tags to fetch JavaScript files from a remote source can be adapted to pull in JSON files from another server as well. To do this, we need to slightly modify the JSON file on the server, however. There are several mechanisms for doing this, one of which is directly supported by jQuery: JSON with Padding, or JSONP. The JSONP file format consists of a standard JSON file that has been wrapped in parentheses and prepended with an arbitrary text string. This string, the "padding", is determined by the client requesting the data. Because of the parentheses, the client can either cause a function to be called or a variable to be set depending on what is sent as the padding string. A PHP implementation of the JSONP technique is quite simple: <?php print($_GET['callback'] .'('. $data .')');?> Here, $data is a variable containing a string representation of a JSON file. When this script is called, the callback query string parameter is prepended to the resulting file that gets returned to the client. To demonstrate this technique, we need only slightly modify our earlier JSON example to call this remote data source instead. The $.getJSON() function makes use of a special placeholder character, ?, to achieve this. $(document).ready(function() { var url = 'http://examples.learningjquery.com/jsonp/g.php'; $('#letter-g a').click(function() { $.getJSON(url + '?callback=?', function(data) { $('#dictionary').empty(); $.each(data, function(entryIndex, entry) { var html = '<div class="entry">'; html += '<h3 class="term">' + entry['term'] + '</h3>'; html += '<div class="part">' + entry['part'] + '</div>'; html += '<div class="definition">'; html += entry['definition']; if (entry['quote']) { html += '<div class="quote">'; $.each(entry['quote'], function(lineIndex, line) { html += '<div class="quote-line">' + line + '</div>'; }); if (entry['author']) { html += '<div class="quote-author">' + entry['author'] + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; $('#dictionary').append(html); }); }); return false; });}); We normally would not be allowed to fetch JSON from a remote server (examples.learningjquery.com in this case). However, since this file is set up to provide its data in the JSONP format, we can obtain the data by appending a query string to our URL, using ? as a placeholder for the value of the callback argument. When the request is made, jQuery replaces the ? for us, parses the result, and passes it to the success function as data just as if this were a local JSON request. Note that the same security cautions hold here as before; whatever the server decides to return to the browser will execute on the user's computer. The JSONP technique should only be used with data coming from a trusted source.
Read more
  • 0
  • 0
  • 1749

article-image-managing-posts-wordpress-plugin
Packt
14 Oct 2009
8 min read
Save for later

Managing Posts with WordPress Plugin

Packt
14 Oct 2009
8 min read
Programming the Manage panel The Manage Posts screen can be changed to show extra columns, or remove unwanted columns in the listing. Let's say that we want to show the post type—Normal, Photo, or Link. Remember the custom field post-type that we added to our posts? We can use it now to differentiate post types. Time for action – Add post type column in the Manage panel We want to add a new column to the Manage panel, and we will call it Type. The value of the column will represent the post type—Normal, Photo, or Link. Expand the admin_menu() function to load the function to handle Manage Page hooks: add_submenu_page('post-new.php', __('Add URL',$this->plugin_domain) , __('URL', $this->plugin_domain) , 1 ,'add-url', array(&$this, 'display_form') );// handle Manage page hooksadd_action('load-edit.php', array(&$this, 'handle_load_edit') );} Add the hooks to the columns on the Manage screen: // Manage page hooksfunction handle_load_edit(){ // handle Manage screen functions add_filter('manage_posts_columns', array(&$this, 'handle_posts_columns')); add_action('manage_posts_custom_column', array(&$this, 'handle_posts_custom_column'), 10, 2);} Then implement the function to add a new Column, remove the author and replace the date with our date format: // Handle Column headerfunction handle_posts_columns($columns){ // add 'type' column $columns['type'] = __('Type',$this->plugin_domain); return $columns;} For date key replacement, we need an extra function:     function array_change_key_name( $orig, $new, &$array ){ foreach ( $array as $k => $v ) $return[ ( $k === $orig ) ? $new : $k ] = $v; return ( array ) $return;} And finally, insert a function to handle the display of information in that column: // Handle Type column displayfunction handle_posts_custom_column($column_name, $id){ // 'type' column handling based on post type if( $column_name == 'type' ) { $type=get_post_meta($id, 'post-type', true); echo $type ? $type : __('Normal',$this->plugin_domain); }} Don't forget to add the Manage page to the list of localized pages: // pages where our plugin needs translation$local_pages=array('plugins.php', 'post-new.php', 'edit.php');if (in_array($pagenow, $local_pages)) As a result, we now have a new column that displays the post type using information from a post custom field. What just happened? We have used the load-edit.php action to specify that we want our hooks to be assigned only on the Manage Posts page (edit.php). This is similar to the optimization we did when we loaded the localization files. The handle_posts_columns is a filter that accepts the columns as a parameter and allows you to insert a new column: function handle_posts_columns($columns){ $columns['type'] = __('Type',$this->plugin_domain); return $columns;} You are also able to remove a column. This example would remove the Author column: unset($columns['author']); To handle information display in that column, we use the handle_posts_custom_column action. The action is called for each entry (post), whenever an unknown column is encountered. WordPress passes the name of the column and current post ID as parameters. That allows us to extract the post type from a custom field: function handle_posts_custom_column($column_name, $id){ if( $column_name == 'type' ) { $type=get_post_meta($id, 'post-type', true); It also allows us to print it out: echo $type ? $type : __('Normal',$this->plugin_domain); }} Modifying an existing column We can also modify an existing column. Let's say we want to change the way Date is displayed. Here are the changes we would make to the code: // Handle Column headerfunction handle_posts_columns($columns){ // add 'type' column $columns['type'] = __('Type',$this->plugin_domain); // remove 'author' column //unset($columns['author']); // change 'date' column $columns = $this->array_change_key_name( 'date', 'date_new', $columns ); return $columns;}// Handle Type column displayfunction handle_posts_custom_column($column_name, $id){ // 'type' column handling based on post type if( $column_name == 'type' ) { $type=get_post_meta($id, 'post-type', true); echo $type ? $type : __('Normal',$this->plugin_domain); } // new date column handling if( $column_name == 'date_new' ) { the_time('Y-m-d <br > g:i:s a'); } }function array_change_key_name( $orig, $new, &$array ){ foreach ( $array as $k => $v ) $return[ ( $k === $orig ) ? $new : $k ] = $v; return ( array ) $return;} The example replaces the date column with our own date_new column and uses it to display the date with our preferred formatting. Manage screen search filter WordPress allows us to show all the posts by date and category, but what if we want to show all the posts depending on post type? No problem! We can add a new filter select box straight to the Manage panel. Time for action – Add a search filter box Let's start by adding two more hooks to the handle_load_edit() function. The restrict_manage_posts function draws the search box and the posts_where alters the database query to select only the posts of the type we want to show. // Manage page hooksfunction handle_load_edit(){ // handle Manage screen functions add_filter('manage_posts_columns', array(&$this, 'handle_posts_columns')); add_action('manage_posts_custom_column', array(&$this, 'handle_posts_custom_column'), 10, 2); // handle search box filter add_filter('posts_where', array(&$this, 'handle_posts_where')); add_action('restrict_manage_posts', array(&$this, 'handle_restrict_manage_posts'));} Let's write the corresponding function to draw the select box: // Handle select box for Manage pagefunction handle_restrict_manage_posts(){ ?> <select name="post_type" id="post_type" class="postform"> <option value="0">View all types</option> <option value="normal" <?php if( $_GET['post_type']=='normal') echo 'selected="selected"' ?>><?php _e ('Normal',$this->plugin_domain); ?></option> <option value="photo" <?php if( $_GET['post_type']=='photo') echo 'selected="selected"' ?>><?php _e ('Photo',$this->plugin_domain); ?></option> <option value="link" <?php if( $_GET['post_type']=='link') echo 'selected="selected"' ?>><?php _e ('Link',$this->plugin_domain); ?></option> </select> <?php} And finally, we need a function that will change the query to retrieve only the posts of the selected type: // Handle query for Manage pagefunction handle_posts_where($where){ global $wpdb; if( $_GET['post_type'] == 'photo' ) { $where .= " AND ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='post-type' AND metavalue='".__ ('Photo',$this->plugin_domain)."' )"; } else if( $_GET['post_type'] == 'link' ) { $where .= " AND ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='post-type' AND metavalue='".__ ('Link',$this->plugin_domain)."' )"; } else if( $_GET['post_type'] == 'normal' ) { $where .= " AND ID NOT IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='post-type' )"; } return $where;} What just happened? We have added a new select box to the header of the Manage panel. It allows us to filter the post types we want to show. We added the box using the restrict_manage_posts action that is triggered at the end of the Manage panel header and allows us to insert HTML code, which we used to draw a select box. To actually perform the filtering, we use the posts_where filter, which is run when a query is made to fetch the posts from the database. if( $_GET['post_type'] == 'photo' ){ $where .= " AND ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='post-type' AND metavalue='".__ ('Photo',$this->plugin_domain)."' )"; If a photo is selected, we inspect the WordPress database postmeta table and select posts that have the post-type key with the value, Photo. At this point, we have a functional plugin. What we can do further to improve it is to add user permissions checks, so that only those users allowed to write posts and upload files are allowed to use it. Quick referencemanage_posts_columns($columns): This acts as a filter for adding/removing columns in the Manage Posts panel. Similarly, we use the function, manage_pages_columns for the Manage Pages panel.manage_posts_custom_column($column, $post_id): This acts as an action to display information for the given column and post. Alternatively, manage_pages_custom_column for Manage Pages panel.posts_where($where): This acts as a filter for the where clause in the query that gets the posts.restrict_manage_posts: This acts as an action that runs at the end of the Manage panel header and allows you to insert HTML.
Read more
  • 0
  • 0
  • 2496
article-image-developing-post-types-plugin-wordpress
Packt
09 Oct 2009
7 min read
Save for later

Developing Post Types Plugin with WordPress

Packt
09 Oct 2009
7 min read
And you will do all of these by developing a Post Types plugin that provide pre-defined post templates to add a photo or a link quickly to your blog. The concepts you will learn in this article will help you discover the not so obvious capabilities of the WordPress platform that allows you to transform it into software—capable of handling much more than just a blog. Handling localization Localization is an important part of WordPress development as not everyone using WordPress speaks English (WordPress comes in different languages too). Localization involves just a small amount of the extra work on your side, since the translation itself is usually done by volunteers (people who like and use your plugin). You only need to provide some base files for translation, and don't be surprised when you start getting translated files sent to your inbox. WordPress uses the GNU gettext localization framework, which is a standardized method of managing translations, and we will make use of it in our plugin. Time for action – Create plugin and add localization We will start by defining our plugin as usual, and then add localization support. Create a new folder called post-types. Create a new post-types.php file with the following content: <?php// pluginname Post Types// shortname PostTypes// dashname post-types/*Plugin Name: Post TypesVersion: 0.1Plugin URI: http://www.prelovac.com/vladimir/wordpress-plugins/post-typesAuthor: Vladimir PrelovacAuthor URI: http://www.prelovac.com/vladimirDescription: Provides pre-defined post templates to quickly add a photo or a link to your blog*/// Avoid name collisions.if ( !class_exists('PostTypes') ) :class PostTypes{ // localization domain var $plugin_domain='PostTypes'; // Initialize the plugin function PostTypes() { global $wp_version; $exit_msg='Post Types requires WordPress 2.5 or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress"> Please update!</a>'; if (version_compare($wp_version,"2.5","<")) { exit ($exit_msg); } } // Set up default values function install() { }}endif;if ( class_exists('PostTypes') ) : $PostTypes = new PostTypes(); if (isset($PostTypes)) { register_activation_hook( __FILE__, array(&$PostTypes, 'install') ); } endif; Adding localization is fairly simple. First we need to add a function to our class that will load the translation file: // Localization supportfunction handle_load_domain(){ // get current language $locale = get_locale(); // locate translation file $mofile = WP_PLUGIN_DIR.'/'.plugin_basename(dirname (__FILE__)).'/lang/' . $this->plugin_domain . '-' . $locale . '.mo'; // load translation load_textdomain($this->plugin_domain, $mofile);} Since loading the file takes resources, we will load it only when the translation is actually needed by checking the current page ($pagenow) and the list of pages pages where we need translations ($local_pages array): // Initialize the pluginfunction PostTypes(){ global $wp_version, $pagenow; // pages where our plugin needs translation $local_pages=array('plugins.php'); if (in_array($pagenow, $local_pages)) $this->handle_load_domain(); $exit_msg='Post Types requires WordPress 2.5 or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress"> Please update!</a>'; Finally, to use the available translations, we only need to enclose our text in the __() function: $this->handle_load_domain();$exit_msg=__('Post Types requires WordPress 2.5 or newer.<a href="http://codex.wordpress.org/Upgrading_WordPress">Please update!</a>', $this->plugin_domain);if (version_compare($wp_version,"2.5","<")) What just happened? We have added localization support to our plugin by using the provided localization functions provided by WordPress. Currently, we have only localized the error message for WordPress version checking: $exit_msg=__('Post Types requires WordPress 2.5 or newer.<a href="http://codex.wordpress.org/Upgrading_WordPress">Please update!</a>', $this->plugin_domain); We have done that by enclosing the text in the __() function, which takes the text as localized, and enclosing our unique localization domain or context within the WordPress localization files. To load localization, we created a handle_load_domain function. The way it works is to first get the current language in use by using the get_locale() function: // Localization supportfunction handle_load_domain(){ // get current language $locale = get_locale(); Then it creates the language file name by adding together the plugin dir, plugin folder, and the lang folder where we will keep the translations. The file name is derived from the locale, and the *.mo language file extension: // locate translation file$mofile = WP_PLUGIN_DIR.'/'.plugin_basename(dirname(__FILE__)).'/lang/' . $this->plugin_domain .'-' . $locale . '.mo'; Finally, the localization file is loaded using the load_textdomain() function, taking our text domain and .mo file as parameters. // load translationload_textdomain($this->plugin_domain, $mofile); Optimizing localization usage The translation file needs to be loaded as the first thing in the plugin—before you output any messages. So we have placed it as the first thing in the plugin constructor. Since loading the translation file occurs at the beginning of the constructor, which is executed every time, it is a good idea to select only the pages where the translation will be needed in order to preserve resources. WordPress provides the global variable, $pagenow, which holds the name of the current page in use. We can check this variable to find out if we are on a page of interest. In the case of plugin activation error message, we want to check if we are on the plugins page defined as plugins.php in WordPress: // pages where our plugin needs translation$local_pages=array('plugins.php');if (in_array($pagenow, $local_pages)) $this->handle_load_domain(); You can optimize this further by querying the page parameter, if it exists, as this will—in most cases—point precisely to the usage of your page (plugins.php?page=photo): if ($_GET['page']=='photo') Optimizing the usage of the translation file is not required; it's just a matter of generally loading only what you need in order to speed up the whole system. How does localization work? For localization to work, you need to provide .po and .mo files with your plugins. These files are created by using external tools such as PoEdit. These tools output the compiled translation file, which can be then loaded by using the load_textdomain() function. This function accepts a language domain name and a path to the file. In order to use translated messages, you can use the __($text, $domain) and _e($text, $domain) functions. The _e() function is just an equivalent of echo __(); These functions accept two parameters, the first being the desired text, and the second, the language domain where the message will be looked for. If no translation was found, the text is just printed out as it is. This means that you can always safely use these functions, even if you do not provide any translation files. This will prepare the plugin for future translation. Quick reference$pagenow: A global variable holding the name of the currently displayed page within WordPress.get_locale(): A function which gets the currently selected language.load_textdomain(domain, filepath): This function loads the localization file and adds it to the specified language domain identifier._(); _e(): These functions are used to find the output text using a given language domain.More information about WordPress localization is available at: http://codex.wordpress.org/Translating_WordPress.
Read more
  • 0
  • 0
  • 2689

article-image-creating-your-own-theme-wordpress-tutorial
Packt
09 Oct 2009
10 min read
Save for later

Creating Your Own Theme - A Wordpress Tutorial

Packt
09 Oct 2009
10 min read
WordPress is the most widely used content management system amongst bloggers for many reasons. Not only does it make site management seem like a walk in the park, but it also uses a type of shared hosting, which means that most users can afford it. It has plug-ins for any occasion and desire and finally, it has themes. For many WordPress users, finding the right theme is a long process that often leads to endless tweaking in the code and stylesheets. However, only a few ever consider learning how to create their own. If you are one of them, this tutorial by Brian Franklin will help you learn how to built and start your own theme. Template files and DIV tags The WordPress theme that waits to be created in this tutorial consists of one stylesheet, one functions file, one comments file and a number of template files. Each template file represents a separate part. Together they form the outline of the website’s overall look. However, it is in the stylesheet that web design elements for each template file are decided. DIV tags are used to define, design, and format values of each template file as well as structure its content and elements. The <div> is often described as an invisible box that is used to separate site content in a structural manner. These are later defined in the stylesheet where size, position, form etc. are assigned.  Template files that will be used in this tutorial are: Index Header Footer Sidebar Single Page And the files that define specific functions for the theme are: Functions Comments DIV tags that will be used in this tutorial are: <div id="wrapper"></div> <div id="header"></div> <div id="container"></div> <div id="footer"></div> <div class="post"></div> <div class="entry"></div> <div class="navigation"></div> <div class="sidebar"></div> Using tags requires proper opening and closing. If any tag is not properly closed (</div>) it may affect the presentation on the entire site. There is difference between a DIV id and a class is what they are used for. Unique site features require definition by ID while values that are used to define several elements are defined as class. In the stylesheet, div id is identified with a # and div class with a . (dot). Install WordPress Before you can start building a theme you need to decide how to work. It is recommended to install WordPress on your local computer. This will allow you to save your changes on a local server rather than dealing with remote server access and uploading. Follow the link for further instructions. The other alternative is installing WordPress through your hosting provider. WordPress hosting is a popular plan offered by most hosts. Either way you need to have WordPress installed, remote or local, and create a folder for your theme under the directory /wp-content/themes/yourtheme. Create the template files Use Dreamweaver, Notepad, EditPlus or any other text editor of your choice and create the template files listed above, including the functions files. Leave them blank for now and save them as PHP files (.php) in your theme folder. index.php Open the index.php, add this piece of code and save the file: <?php get_header(); ?><div id="container"> <?php if(have_posts()) : ?><?php while(have_posts()) : the_post(); ?> <div class="post" id="post-<?php the_ID(); ?>"> <h2><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"> <?php the_title(); ?></a></h2> <div class="entry"> <?php the_content(); ?> <p class="postmetadata"><?php _e('Filed under&#58;'); ?> <?php the_category(', ') ?> <?php _e('by'); ?> <?php the_author(); ?><br /><?php comments_popup_link('No Comments &#187;', '1 Comment &#187;', '% Comments &#187;'); ?> <?php edit_post_link('Edit', ' &#124; ', ''); ?> </p> </div> </div> <?php endwhile; ?> <div class="navigation"> <?php posts_nav_link(); ?> </div> <?php endif; ?></div><?php get_sidebar(); ?><?php get_footer(); ?> The index-file now contains the code that calls to the header, sidebar and footer template file. But since there is no code to define these template files yet, you will not see them when previewing the site. The index file is the first file the web browser will call when requesting your site. This file defines the site’s frontpage and thus also includes DIV ID tags "container" and DIV classes "post" and "entry". These elements have been included to structure your frontpage content; posts and post entries. If you preview your WordPress site you should see your three latest blog posts, including ‘next’ and ‘previous’ buttons to the remaining ones. To shorten the length of displayed frontpage post, simply log in to the WordPress admin and insert a page break where you want the post to display a Read more link. You will come back to index.php in a moment, but for now you can save and close the file. It is now time to create the remaining template files. header.php Open header.php, add this code and save the file: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html ><head profile="http://gmpg.org/xfn/11"> <title><?php bloginfo('name'); ?><?php wp_title(); ?></title> <meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" /> <meta name="generator" content="WordPress <?php bloginfo('version'); ?>" /> <!-- leave this for stats please --> <link rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>" type="text/css" media="screen" /> <link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="<?php bloginfo('rss2_url'); ?>" /> <link rel="alternate" type="text/xml" title="RSS .92" href="<?php bloginfo('rss_url'); ?>" /> <link rel="alternate" type="application/atom+xml" title="Atom 0.3" href="<?php bloginfo('atom_url'); ?>" /> <link rel="pingback" href="<?php bloginfo('pingback_url'); ?>" /> <?php wp_get_archives('type=monthly&format=link'); ?> <?php //comments_popup_script(); // off by default ?> <?php wp_head(); ?></head><body><div id="wrapper"><div id="header"><h1><a href="<?php bloginfo('url'); ?>"><?php bloginfo('name'); ?></a></h1><?php bloginfo('description'); ?></div> The header template file now has the opening tags <html> and <body>. Included in the <html> tag is the <head> tag that holds the meta tags and stylesheet links. The body tag includes two DIV ID tags; wrapper and header that define the boxes and hold the overall position of the site and the header content. footer.php Open footer.php, add the code below and save the file: <div id="footer"> <p>Copyright 2007 <a href="<?php bloginfo('url'); ?>"><?php bloginfo('name'); ?></a></p></div></div></body></html> The footer is a template that defines the bottom part of the website. The footer for this theme now holds a Copyright announcement and a php code that adds the name of the blog as a permalink. As it is the last template file to be called for a site it also closes the body and html tag. sidebar.php Open sidebar.php, add the code below and save the file: <div class="sidebar"><ul><?php if ( function_exists('dynamic_sidebar') && dynamic_sidebar() ) : else : ?> <?php wp_list_pages('depth=3&title_li=<h2>Pages</h2>'); ?> <li><h2><?php _e('Categories'); ?></h2> <ul> <?php wp_list_cats('sort_column=name&optioncount=1&hierarchical=0'); ?> </ul> </li> <li><h2><?php _e('Archives'); ?></h2> <ul> <?php wp_get_archives('type=monthly'); ?> </ul> </li> <?php get_links_list(); ?> <?php endif; ?></ul></div> The sidebar template is now defined and includes the site’s pages, categories, archive and blogroll. Study the code and see the changes in the web browser. Regarding the DIV class, it is left to be designed in the stylesheet. single.php Open single.php, add the code below and save the file: <?php get_header(); ?><div id="container"> <?php if(have_posts()) : ?><?php while(have_posts()) : the_post(); ?> <div class="post" id="post-<?php the_ID(); ?>"> <h2><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"> <?php the_title(); ?></a></h2> <div class="entry"> <?php the_content(); ?> <p class="postmetadata"><?php _e('Filed under&#58;'); ?> <?php the_category(', ') ?> <?php _e('by'); ?> <?php the_author(); ?><br /><?php comments_popup_link('No Comments &#187;', '1 Comment &#187;', '% Comments &#187;'); ?> <?php edit_post_link('Edit', ' &#124; ', ''); ?> </p> </div> <div class="comments-template"><?php comments_template(); ?></div> </div> <?php endwhile; ?> <div class="navigation"> <?php previous_post_link('%link') ?> <?php next_post_link(' %link') ?> </div> <?php endif; ?></div><?php get_sidebar(); ?><?php get_footer(); ?> The single.php template file specifically defines the elements on the single post page that is different from both the frontpage post listing and pages. The code above is basically a copy/paste from the index-file, only with minor changes to the Next and Previous links. 
Read more
  • 0
  • 0
  • 3586

article-image-social-bookmarking-wordpress-plugin
Packt
08 Oct 2009
6 min read
Save for later

Social Bookmarking with WordPress Plugin

Packt
08 Oct 2009
6 min read
You will learn these by creating a Social Bookmarking type of plugin that adds a Digg button to each post on your blog As you probably know, Digg is a very popular service for promoting interesting content on the Internet. The purpose of a Digg button on your blog is to make it easier for Digg users to vote for your article and also to bring in more visitors to your blog. The plugin we'll create in this article will automatically insert the necessary code to each of your posts. So let's get started with WordPress plugin development! Plugging in your first plugin Usually, the first step in plugin creation is coming up with a plugin name. We usually want to use a name that is associated with what the plugin does, so we will call this plugin, WP Digg This. WP is a common prefix used to name WordPress plugins. To introduce the plugin to WordPress, we need to create a standard plugin header. This will always be the first piece of code in the plugin file and it is used to identify the plugin to WordPress. Time for action – Create your first plugin In this example, we're going to write the code to register the plugin with WordPress, describe what the plugin does for the user, check whether it works on the currently installed version of WordPress, and to activate it. Create a file called wp-digg-this.php in your favorite text editor. It is common practice to use the plugin name as the name for the plugin file, with dashes '-' instead of spaces. Next, add a plugin information header. The format of the header is always the same and you only need to change the relevant information for every plugin: <?php /* Plugin Name: WP Digg This Version: 0.1 Description: Automatically adds Digg This button to your posts. Author: Vladimir Prelovac Author URI: http://www.prelovac.com/vladimir Plugin URI: http://www.prelovac.com/vladimir/wordpress-plugins/ wp-digg-this */ ?> Now add the code to check the WordPress version: /* Version check */ global $wp_version; $exit_msg='WP Digg This requires WordPress 2.5 or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress">Please update!</a>'; if (version_compare($wp_version,"2.5","<")) { exit ($exit_msg); } ?> Upload your plugin file to the wp-content/plugins folder on your server using your FTP client. Go to your WordPress Plugins admin panel. You should now see your plugin listed among other plugins: This means we have just completed the necessary steps to display our plugin in WordPress. Our plugin can be even activated now—although it does not do anything useful (yet). What just happened? We created a working plugin template by using a plugin information header and the version check code. The plugin header allows the plugin to be identified and displayed properly in the plugins admin panel. The version check code will warn users of our plugin who have older WordPress versions to upgrade their WordPress installation and prevent compatibility problems. The plugin information header To identify the plugin to WordPress, we need to include a plugin information header with each plugin. The header is written as a PHP comment and contains several fields with important information. This code alone is enough for the plugin to be registered, displayed in the admin panel and readied for activation. If your future plugin has more than one PHP file, the plugin information should be placed only in your main file, the one which will include() or require()  the other plugin PHP files. Checking WordPress versions To ensure that our plugin is not activated on incompatible WordPress versions, we will perform a simple WordPress version check at the very beginning of our code. WordPress provides the global variable $wp_version that provides the current WordPress version in standard format. We can then use PHP function version_compare() to compare this and our required version for the plugin, using the following code: if (version_compare($wp_version,"2.6","<")) { // do something if WordPress version is lower then 2.6 } If we want to stop the execution of the plugin upon activation, we can use the exit() function with the error message we want to show. In our case, we want to show the required version information and display the link to the WordPress upgrade site. $exit_msg='WP Digg This requires WordPress 2.6 or newer. <a href="http://codex.wordpress.org/Upgrading_WordPress">Please update!</a>'; if (version_compare($wp_version,"2.6","<")) { exit ($exit_msg); } While being simple, this piece of code is also very effective. With the constant development of WordPress, and newer versions evolving relatively often, you can use version checking to prevent potential incompatibility problems. The version number of your current WordPress installation can be found in the footer text of the admin menu. To begin with, you can use that version in your plugin version check (for example, 2.6). Later, when you learn about WordPress versions and their differences, you'll be able to lower the version requirement to the minimal your plugin will be compatible with. This will allow your plugin to be used on more blogs, as not all blogs always use the latest version of WordPress. Checking the plugin You can go ahead and activate the plugin. The plugin will be activated but will do nothing at this moment. Time for Action – Testing the version check Deactivate the plugin and change the version check code to a higher version. For example, replace 2.6 with 5.0. if (version_compare($wp_version,"5.0","<")) Re-upload the plugin and try to activate it again. You will see a WordPress error and a message from the plugin: What just happened? The version check fails and the plugin exits with our predefined error message. The same thing will happen to a user trying to use your plugin with outdated WordPress installation, requiring them to update to a newer version. Have a go Hero We created a basic plugin that you can now customize. Change the plugin description to include HTML formatting (add bold or links to the description). Test your plugin to see what happens if you have two plugins with the same name (upload a copy of the file under a different name).
Read more
  • 0
  • 0
  • 2623