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 - CMS & E-Commerce

830 Articles
article-image-integrating-twitter-magento
Packt
14 Jan 2011
2 min read
Save for later

Integrating Twitter with Magento

Packt
14 Jan 2011
2 min read
Integrating your Magento website with Twitter is a useful way to stay connected with your customers. You'll need a Twitter account (or more specifically an account for your business)  but once that's in place it's actually pretty easy. Adding a 'Follow Us On Twitter' button to your Magento store One of the more simple ways to integrate your store's Twitter feed with Magento is to add a 'Follow Us On Twitter' button to your store's design. Generating the markup from the Twitter website Go to the Twitter Goodies website (): Select the Follow Buttons option and then select the Looking for Follow us on Twitter buttons? towards the bottom of the screen: The buttons will now change to the FOLLOW US ON Twitter buttons: Select the style of button you'd like to make use of on your Magento store and then select the generated HTML that is provided in the pop-up that is displayed: The generated HTML for the M2 Store's Twitter account (with the username of M2MagentoStore) looks like the following: <a href="http://www.twitter.com/M2MagentoStore"> <img src="http://twitter-badges.s3.amazonaws.com/follow_us-a.png" alt="Follow M2MagentoStore on Twitter"/> </a> Adding a static block in Magento for your Twitter button Now you will need to create a new static block in the Magento CMS feature: navigate to CMS Static Blocks| in your Magento store's administration panel and click on Add New Block. As you did when creating a static block for the supplier logos used in your store's footer, complete the form to create the new static block. Add the Follow Us On Twitter button to the Content field by disabling the Rich Text Editor with the Show/Hide Editor button and pasting in the markup you generated previously: You don't need to upload an image to your store through Magento's CMS here as the Twitter buttons are hosted elsewhere. Note that the Identifier field reads follow-twitter—you will need this for the layout changes you are about to make!
Read more
  • 0
  • 0
  • 3220

article-image-drupal-intranets-open-atrium-creating-dashboard
Packt
05 Jan 2011
7 min read
Save for later

Drupal Intranets with Open Atrium: Creating Dashboard

Packt
05 Jan 2011
7 min read
Drupal Intranets with Open Atrium Discover an intranet solution for your organization with Open Atrium Unlock the features of Open Atrium to set up an intranet to improve communication and workflow Explore the many features of Open Atrium and how you can utilize them in your intranet Learn how to support, maintain, and administer your intranet A how-to guide written for non-developers to learn how to set up and use Open Atrium   Main dashboard The main dashboard provides an interface for managing and monitoring our Open Atrium installation. This dashboard provides a central place to monitor what's going on across our departments. It will also be used as the central gateway for most of our administrative tasks. From this screen we can add groups, invite users, and customize group dashboards. Each individual who logs in also has the main dashboard and can quickly glance at the overall activity for their company. The dashboard is laid out initially by default in a two column layout. The left side of the screen contains the Main Content section and the right side of the screen contains a Sidebar. In a default installation of Open Atrium, there will be a welcome video in the Main Content area on the left. The first thing that you will notice when you log in is that there is a quick video that you can play on your main dashboard screen. This video provides a quick overview of Open Atrium for our users, and a review of the options you have for working with the dashboard. In the following screenshot, you will see the main dashboard and how the two separate content areas are divided, with a specific section marked that we will discuss later in the article: Each dashboard can be customized to either a two-column or split layout, as shown in the preceding screenshot, or a three-column layout. Under the Modifying Layout section of this article, we will cover how to change the overall layout. As you can see in the preceding image, the dashboard is divided into three distinct sections. There is the header area which includes the Navigation tabs for creating content, modifying settings, and searching the site. Under the header area, we have the main content and sidebar areas. These areas are made up of blocks of content from the site. These blocks can bring forward and include different items depending on how we customize our site. For example, in the left column we could choose to display Recent Activity and Blog Posts, while the right column could show Upcoming Events and a Calendar. Any of the features that we find throughout Open Atrium can be brought forward to a dashboard page. The beauty of this setup is that each group can customize their own. In the next section of this article, we will cover group dashboards in more detail. However, the same basic concepts will apply to all the dashboards. After our users are comfortable with using Open Atrium, we may decide that we no longer need to show the tutorial video on the main dashboard. This video can be easily removed by clicking on the Customizing the dashboard link just above the Recent Activity block or by clicking on the Customize dashboard link on the top right in the header section. Click on the customizing the dashboard link and we will see a dashboard widget on the screen. This will be the main interface for configuring layout and content on our dashboard. Now, hover over the video and on the top right you will see two icons. The first icon that looks like a plus sign (+) indicates that the content can be dragged. We can click on this icon when hovering over a section of content and move that content to another column or below another section of content on our dashboard. The X indicates that we can remove that item from our dashboard. Hovering over any piece of content when you are customizing the dashboard should reveal these two icons. The two icons are highlighted in the following screenshot with a square box drawn around them: To remove the welcome video, we click on the red X and then click on Save changes and the video tutorial will be removed from the dashboard. Group dashboard The group dashboard works the same as the main dashboard. The only difference is that the group dashboard exposes content for the individual departments or groups that are setup on our site. For example, a site could have a separate group for the Human Resources, Accounting, and Management departments. Each of these groups can create a group dashboard that can be customized by any of the administrators for a particular group. The following screenshot shows how the Human Resources department has customized their group dashboard:   In the preceding screenshot, we can see how the HR department customized their dashboard. In the left column they have added a Projects and a Blog section. The Projects section links to specific projects within the site, and the Blog section links to the detailed blog entries. There is also a customized block in the right column where the HR department has added the Upcoming events, a Mini calendar, and a Recent activity block. The Projects section is a block that is provided by the system and exposes content from the Case tracker or Todo sections of the HR website. The Upcoming events section is a customized block that highlights future events entered through the calendar feature. To demonstrate how each department can have a different dashboard, the following screenshot shows the dashboard for the Accounting department: The Accounting dashboard has been configured to show a custom block as the first item in the left column, and below that a listing of Upcoming events. In the right column, the Accounting administrator has added a block which brings forward the Latest cases of all the latest cases, exposing the most recent issues entered into the tracking system. It is also worth noting that the Accounting department has a completely different color scheme from the Human Resources department. The color scheme can be changed by clicking on Settings | Group Settings | Features. We can scroll down to the bottom of the screen and click on BACKGROUND to either enter a hexadecimal color for our main color or pick a color from the color wheel as displayed in the following screenshot: Spaces Spaces is a Drupal API module that allows sitewide configurable options to be overridden by individual spaces. The spaces API is included with our Open Atrium installation and provides the foundation for creating group and user configurable dashboards. Users can then customize their space and set settings that are only applied to their space. This shows the extreme power and flexibility of Open Atrium by allowing users to apply customizations without affecting any of the other areas of Open Atrium. Users can use the functionality provided by spaces to create an individualized home page. Group spaces Group spaces provide an area for each group or department to arrange content in a contextual manner that makes sense for each group. In the preceding examples, the content that is important to the accounting department is not necessarily important to the human resources department. Administrators of each department can take advantage of Open Atrium's complete flexibility to arrange content in a way that works for them. The URLs in the example that we have been looking at are listed as follows: Human Resources: http://acme.alphageekdev.com/hr. Accounting: http://acme.alphageekdev.com/acct Each URL is composed of the site URL, that is, http://acme.alphageekdev.com/ and then the short name that we provided for our group space, hr and acct. User spaces User spaces work in the same way that the group dashboard and spaces work. Each user of the system can customize their dashboard any way that they see appropriate. The following screenshot shows an example of the user's dashboard for the admin account: In the preceding screenshot, we have drawn a box around two areas. These two areas represent two different group spaces showing on the user's dashboard page. This shows how content can be brought forward to various dashboards to show only what is important to a particular user.
Read more
  • 0
  • 0
  • 2041

article-image-faqs-mahara-12-eportfolios
Packt
04 Jan 2011
7 min read
Save for later

FAQs on Mahara 1.2 ePortfolios

Packt
04 Jan 2011
7 min read
Mahara 1.2 E-Portfolios: Beginner's Guide Create and host educational and professional e-portfolios and personalized learning communities Create, customize, and maintain an impressive personal digital portfolio with a simple point-and-click interface Set customized access to share your text files, images, and videos with your family, friends, and others Create online learning communities and social networks through groups, blogs, and forums A step-by-step approach that takes you through examples with ample screenshots and clear explanations Q: What will you need to install Mahara? A: Before you can install Mahara, you will need to have access to a Linux server. It may be that you run Linux on a laptop or desktop at home or that your company or institution has its own Linux servers, in which case, great! If not, there are many hosting services available on the Internet, which will enable you to access a Linux server and therefore run Mahara. It is important that you get a server to which you have root access. It is also important that you set your server up with the following features: Database: Mahara must have a database to work. The databases supported are PostgreSQL Version 8.1 or later and MySQL Version 5.0.25 or later. The Mahara developers recommend that you use PostgreSQL, if possible, but for most installations, MySQL will work just as well. PHP: Mahara requires PHP Version 5.1.3 or later. Web Server: The preferred web server is Apache. PHP extensions: Compulsory Extensions: GD, JSON, cURL, libxml, SimpleXML, Session, pgSQL or Mysqli, EXIF, OpenSSL or XML-RCP (for networking support) Optional Extension: Imagick Q: Can Mahara be installed on Windows? A: At the moment, the Mahara developers offer no support for running Mahara on Windows Servers. It is designed to primarily work with Linux, Apache, PHP, and open source SQL databases. Q: What are Views in Mahara? How are they different from Blogs? A: They are one of the stand-out features of Mahara and we think you are really going to enjoy learning to use them. Views, like blogs, are an excellent tool for reflection. The difference between the two is that a blog is very text orientated with a user reflecting on a topic in writing (with usually an odd image or video to supplement the text), but Views allow you to express your ideas in more of a "web page" type format using lots of different blocks. Also, Views are flexible; you can very easily add and remove whichever blocks you want. Q: What are some of the things you can do with Views in Mahara? A: Let's think about some of the things you can do with Views in Mahara: You could present all of your ideas related to one of the topics in a qualification you are taking. This could be for your own reference or you may choose to share access to this View with your tutor or classmates. You could use a View to take notes on all of the thoughts, ideas, links, and so on, that you gather while you are attending a conference (if you have a wireless connection). You can then share the View with your colleagues after the event to show them what you have learned. You could use a View to explore and express your thoughts on a particular aspect of your social or family life, such as a family holiday. This is likely to be private, and something you would only share with other members of your family. You could use a View as a tutor to present all of the important materials your learners need to read, watch, listen to, and think about in preparation for a particular topic they are going to study with you. Lots of lecturers prefer to use Mahara to present their work instead of doing so in a Virtual Learning Environment such as Moodle. This may be partly because the lecturer's name (and avatar) will continue to be associated with the work presented in the Mahara View even after they retire or move on to another academic institution. You could use a View to present an ongoing progress report on a project you are doing at work. You might make a blog post an element of this View as well as make important files related to your project available for sharing. Q: What is a secret URL? A: It is used if you would like to give some people who aren't already members of the Mahara site access to your View. The URL is simply a link to the View, which you can set up as a hyperlink in another web page, in a blog, or e-mail so that others can open it. The URL that is created is difficult to guess so that the general public can't see your View. Rather than use it as a hyperlink, you could just send the whole link to the people you would like to give access to the View by pasting it into an e-mail, for example. Q: When can the View feedback be useful? A: This might be useful in the following situations: You might have asked a peer for feedback on some work you are doing on a particular course in exchange for feedback you can give on their work. A tutor may have added your View to their Watchlist. You may then get some informal feedback from your tutor on your work before you submit it for formal assessment. You could be using the feedback functionality as a communication vehicle. You may raise a topic for discussion with your workmates, for example, and get them to answer the core question(s) posed in your View by using the feedback option. You may have used a View to share highlights of a recent holiday experience with your friends in Mahara. They could then use the feedback option to tell you how jealous they are of your rich experiences or at least of your suntan! Q: How can the Mahara partners help you? A: Mahara partners can help you with hosting, theming, training, coding, tweaking, extending, bug-fixing, problem-resolving, implementation consultancies, and, well, just about anything to do with Mahara, if you ask them nicely enough. All Mahara partners are excellent support agencies and, if you ARE really keen on using Mahara, you really should give one of the partners a shout. Q: What are the steps to join the Mahara Community? A: The steps to join the Mahara Community are as follows: Go and register: The very first thing that anyone will do on your Mahara site is log in. Head on over to http://mahara.org and click the option to Register, a small link, which you will find in the pale blue Login butt on in the top right-hand part of the screen. Once you've registered you can log in. Respond to your e-mail: You will need to confirm your registration by clicking the link that has been sent to your e-mail address. Once you have done this, you will find yourself at http://mahara.org, which is itself a Mahara site. Let's find some people!: Click on the Community tab, and then on the Members tab. Now let's see if you can find the authors! Can you see the Query box? Type in my name Derrin and see if you can find me? Let's look at some views: Now you've found me, click on my name and why not click on one of my views? Here's another example of Mahara in action. Join a forum: Click on the Community tab again, and now on the Forums tab. Can you see the Support forum? Its description is Need help using or installing Mahara? Ask your questions here. That's going to be useful to you, I bet! Why not subscribe to this forum by clicking on the Subscribe button. You will now be e-mailed with all the updates to this forum. Maybe there are other forums you might want to subscribe to. If you just want to browse a forum, just click on the name of the forum and you will be taken to a list of the posts. Have a look at the Mahara partners: Click on the Partners tab.
Read more
  • 0
  • 0
  • 1256
Visually different images

article-image-drupal-faqs
Packt
30 Dec 2010
5 min read
Save for later

Drupal FAQs

Packt
30 Dec 2010
5 min read
  Drupal 7 First Look Learn the new features of Drupal 7, how they work and how they will impact you Get to grips with all of the new features in Drupal 7 Upgrade your Drupal 6 site, themes, and modules to Drupal 7 Explore the new Drupal 7 administration interface and map your Drupal 6 administration interface to the new Drupal 7 structure Complete coverage of the DBTNG database layer with usage examples and all API changes for both Themes and Modules Also includes an Appendix that introduces the beta release for Drupal 7. It is not a part of the book (print or e-book) but only available for free download Appendix         Read more about this book       Q: What is Drupal?A: Drupal is an Open Source Content Management System used for building dynamic websites.   Q: Why should I use Drupal and not any other CMS?A: By building on relevant standards and open source technologies, Drupal supports and enhances the potential of the Internet as a medium where diverse and geographically separated individuals and groups can collectively produce, discuss, and share information and ideas. With a central interest in and focus on communities and collaboration, Drupal's flexibility allows the collaborative production of online information systems and communities.   Q: What are the minimum requirements for Drupal 7?A: Drupal 7 requires PHP 5.2.0 or later to run the Drupal code. You will also need one of the following databases to run Drupal 7: MySQL version 5.0 or later PostgreSQL 8.3 or later SQLite 3.4.2 or later   Q: Where can one download Drupal 7 from?A: Head on over to http://drupal.org/project/drupal and click on the Drupal version number you wish to download—in this case it is Drupal 7. Click on Download and then save it to your C: drive or your My Documents folder (or wherever you want).   Q: What's new in Drupal 7?A: There are several key functionalities that made it to Drupal 7. Some of them are as follows: New administration toolbar and overlay administration: After installing Drupal 7 you will notice the new administration toolbar (shown in the following screenshot) that appears on all pages if you have the permission to administer the site: (Move the mouse over the image to enlarge it.) The toolbar groups commonly used tasks together making it easier for new administrators to learn how to configure Drupal and making it quicker for experienced administrators to get to commonly-used functionality. New Field API: The Field API allows site administrators to add additional attributes to a node type. It also supports translatable fields to allow for multi-lingual sites. Added context information to messages during translation: Drupal 7 adds an optional context for the translation to allow developers and themers to make translatable strings less ambiguous. Built-in automated cron functionality: Drupal 7 includes a new cron system that does not rely on running cron from the Unix cron system. The mechanism used is similar to the one used by poormanscron except that it runs from an AJAX request rather than delaying the response time of the page triggering cron. Added a new plugin manager: The plugin manager allows automatic updates of your Drupal installation. Seven theme for administration: A common complaint of Drupal administrators in previous versions was the look of the administration interface and that it could be difficult to tell when you were in the administration interface, since it used the same theme as regular content by default. To fix this, Drupal 7 has added a new administration theme called the Seven theme that is enabled by default. jQuery UI to core: jQuery UI (http://jqueryui.com) is a powerful JavaScript library that includes common controls like calendars, progress bars, tabs, sliders, and more. It also includes functionality to allow drag and drop, resizing, sorting, selection, and more. New Stark theme: The new Stark theme that is designed to make it easier to learn how to build a custom theme. Rewritten database layer (DBTNG): The biggest change in Drupal 7, at least for developers, is the new database layer, also called DBTNG (short for Database Layer: The Next Generation). DBTNG is a big change for developers since it changes how modules interact with the database. Queue API for long-running tasks: Drupal 7 adds a Queue API to manage long-running tasks. In general, any task that takes more than 30 seconds to a minute would be an excellent candidate for the Queue API. New test framework: Drupal 7 adds a comprehensive test framework called testing that allows developers and site administrators to run tests against an existing Drupal installation to ensure that it is behaving properly.   Q: How has the installation process improved in Drupal 7?A: Drupal 7 has a new installation routine. It is designed to make it easier for new Drupal users to set up Drupal. The new installation offers two types of install—the regular installation and a minimal installation.   The Minimal installation is similar to previous versions. The new Standard installation automatically enables commonly-used functionality during the installation to save time after setup.   Q: How has the interface for creating content and new content types improved in Drupal 7?A: Improved interface for creating content: A big, but welcome, change for editors is the redesigned and updated interface to create and edit content. A sample of the interface is shown in the following screenshot:   The redesigned screen makes it easier to quickly navigate to specific sections within the content. Improved interface for creating new content types: The interface for creating content types has been redesigned to keep all of the options in a smaller space so navigation is easier and all information can be quickly accessed.
Read more
  • 0
  • 0
  • 1500

Packt
16 Dec 2010
15 min read
Save for later

Page Management – Part Two in CMS

Packt
16 Dec 2010
15 min read
  CMS Design Using PHP and jQuery Build and improve your in-house PHP CMS by enhancing it with jQuery Create a completely functional and a professional looking CMS Add a modular architecture to your CMS and create template-driven web designs Use jQuery plugins to enhance the "feel" of your CMS A step-by-step explanatory tutorial to get your hands dirty in building your own CMS         Read more about this book       (For more resources on this subject, see here.) Dates Dates are annoying. The scheme I prefer is to enter dates the same way MySQL accepts them—yyyy-mm-dd hh:mm:ss. From left to right, each subsequent element is smaller than the previous. It's logical, and can be sorted sensibly using a simple numeric sorter. Unfortunately, most people don't read or write dates in that format. They'd prefer something like 08/07/06. Dates in that format do not make sense. Is it the 8th day of the 7th month of 2006, or the 7th day of the 8th month of 2006, or even the 6th day of the 7th month of 2008? Date formats are different all around the world. Therefore, you cannot trust human administrators to enter the dates manually. A very quick solution is to use the jQuery UI's datepicker plugin. Temporarily (we'll remove it in a minute) add the highlighted lines to /ww.admin/pages/pages.js: other_GET_params:currentpageid }); $('.date-human').datepicker({ 'dateFormat':'yy-mm-dd' });}); When the date field is clicked, this appears: It's a great calendar, but there's still a flaw: Before you click on the date field, and even after you select the date, the field is still in yyyy-mm-dd format. While MySQL will thank you for entering the date in a sane format, you will have people asking you why the date is not shown in a humanly readable way. We can't simply change the date format to accept something more reasonable such as "May 23rd, 2010", because we would then need to ensure that we can understand this on the server-side, which might take more work than we really want to do. So we need to do something else. The datepicker plugin has an option which lets you update two fields at the same time. This is the solution—we will display a dummy field which is humanly readable, and when that's clicked, the calendar will appear and you will be able to choose a date, which will then be set in the human-readable field and in the real form field. Don't forget to remove that temporary code from /ww.admin/pages/pages.js. Because this is a very useful feature, which we will use throughout the admin area whenever a date is needed, we will add a global JavaScript file which will run on all pages. Edit /ww.admin/header.php and add the following highlighted line: <script src="/j/jquery.remoteselectoptions /jquery.remoteselectoptions.js"></script><script src="/ww.admin/j/admin.js"></script><link rel="stylesheet" href="http://ajax.googleapis.com /ajax/libs/jqueryui/1.8.0/themes/south-street /jquery-ui.css" type="text/css" /> And then we'll create the /ww.admin/j/ directory and a file named /ww.admin/j/admin.js: function convert_date_to_human_readable(){ var $this=$(this); var id='date-input-'+Math.random().toString() .replace(/\./,''); var dparts=$this.val().split(/-/); $this .datepicker({ dateFormat:'yy-mm-dd', modal:true, altField:'#'+id, altFormat:'DD, d MM, yy', onSelect:function(dateText,inst){ this.value=dateText; } }); var $wrapper=$this.wrap( '<div style="position:relative" />'); var $input=$('<input id="'+id+'" class="date-human-readable" value="'+date_m2h($this.val())+'" />'); $input.insertAfter($this); $this.css({ 'position':'absolute', 'opacity':0 }); $this .datepicker( 'setDate', new Date(dparts[0],dparts[1]-1,dparts[2]) );}$(function(){ $('input.date-human').each(convert_date_to_human_readable);}); This takes the computer-readable date input and creates a copy of it, but in a human-readable format. The original date input box is then made invisible and laid across the new one. When it is clicked, the date is updated on both of them, but only the human-readable one is shown. Much better. Easy for a human to read, and also usable by the server. Saving the page We created the form, and except for making the body textarea more user-friendly, it's just about finished. Let's do that now. When you click on the Insert Page Details button (or Update Page Details, if an ID was provided), the form data is posted to the server. We need to perform these actions before the page menu is displayed, so it is up-to-date. Edit /ww.admin/pages.php, and add the following highlighted lines before the load menu section: echo '<h1>Pages</h1>';// { perform any actionsif(isset($_REQUEST['action'])){ if($_REQUEST['action']=='Update Page Details' || $_REQUEST['action']=='Insert Page Details'){ require 'pages/action.edit.php'; } else if($_REQUEST['action']=='delete'){ 'pages/action.delete.php'; }}// }// { load menu If an action parameter is sent to the server, then the server will use this block to decide whether you want to edit or delete the page. We'll handle deletes later in the article. Notice that we are handling inserts and updates with the same file, action.edit.php—in the database, there is almost no difference between the two when using MySQL. So, let's create that file now. We'll do it a bit at a time, like how we did the form, as it's a bit long. Create /ww.admin/pages/action.edit.php with this code: <?phpfunction pages_setup_name($id,$pid){ $name=trim($_REQUEST['name']); if(dbOne('select id from pages where name="'.addslashes($name).'" and parent='.$pid.' and id!='.$id,'id')){ $i=2; while(dbOne('select id from pages where name="'.addslashes($name.$i).'" and parent='.$pid.' and id!='.$id,'id'))$i++; echo '<em>A page named "'.htmlspecialchars($name).'" already exists. Page name amended to "' .htmlspecialchars($name.$i).'".</em>'; $name=$name.$i; } return $name;} The first piece is a function which tests the submitted page name. If that name is the same as another page which has the same parent, then a number is added to the end and a message is shown explaining this. Here's an example, creating a page named "Home" in the top level (we already have a page named "Home"): Next we'll create a function for testing the inputted special variable. Add this to the same file: function pages_setup_specials($id=0){ $special=0; $specials=isset($_REQUEST['special']) ?$_REQUEST['special']:array(); foreach($specials as $a=>$b) $special+=pow(2,$a); $homes=dbOne("SELECT COUNT(id) AS ids FROM pages WHERE (special&1) AND id!=$id",'ids'); if($special&1){ // there can be only one homepage if($homes!=0){ dbQuery("UPDATE pages SET special=special-1 WHERE special&1"); } } else{ if($homes==0){ $special+=1; echo '<em>This page has been marked as the site\'s Home Page, because there must always be one.</em>'; } } return $special;} In this function, we build up the special variable, which is a bit field. A bit field is a number which uses binary math to combine a few "yes/no" answers into one single value. It's good for saving space and fields in the database.Each value has a value assigned to it which is a power of two. The interesting thing to note about powers of two is that in binary, they're always represented as a 1 with some 0s after it. For example, 1 is represented as 00000001, 2 is 00000010, 4 is 00000100, and so on.When you have a bit field such as 00000011 (each number here is a bit), it's easy to see that this is composed of the values 1 and 2 combined, which are 20 and 21 respectively.The & operator lets us check quickly if a certain bit is turned on (is 1) or not. For example, 00010011 & 16 is true, and 00010011 & 32 is false, because the 16 bit is on and the 32 bit is off. In the database, we set a bit for the homepage, which we say has a value of 1. In the previous function, we need to make sure that after inserting or updating a page, there is always exactly one homepage. The only other one we've set so far is "does not appear in navigation menu", which we've given the value 2. If we added a third bit flag ("is a 404 handler", for example), it would have the value 4, then 8, and so on. Okay—now we will set up our variables. Add this to the same file: // { set up common variables$id =(int)$_REQUEST['id'];$pid =(int)$_REQUEST['parent'];$keywords =$_REQUEST['keywords'];$description =$_REQUEST['description'];$associated_date =$_REQUEST['associated_date'];$title =$_REQUEST['title'];$name =pages_setup_name($id,$pid);$body =$_REQUEST['body'];$special =pages_setup_specials($id);if(isset($_REQUEST['page_vars'])) $vars =json_encode($_REQUEST['page_vars']);else $vars='[]';// } Then we will add the main body of the page update SQL to the same file: // { create SQL$q='edate=now(),type="'.addslashes($_REQUEST['type']).'", associated_date="'.addslashes($associated_date).'", keywords="'.addslashes($keywords).'", description="'.addslashes($description).'", name="'.addslashes($name).'", title="'.addslashes($title).'", body="'.addslashes($body).'",parent='.$pid.', special='.$special.',vars="'.addslashes($vars).'"';// } This is SQL which is common to both creating and updating a page. Finally we run the actual query and perform the action. Add this to the same file: // { run the queryif($_REQUEST['action']=='Update Page Details'){ $q="update pages set $q where id=$id"; dbQuery($q);}else{ $q="insert into pages set cdate=now(),$q"; dbQuery($q); $_REQUEST['id']=dbLastInsertId();}// }echo '<em>Page Saved</em>'; In the first case, we simply run an update. In the second, we run an insert, adding the creation date to the query, and then setting $_REQUEST['id'] to the ID of the entry that we just created. Creating new top-level pages If you've been trying all this, you'll have noticed that you can create a top-level page simply by clicking on the admin area's Pages link in the top menu, and then you're shown an empty Insert Page Details form. If you've been trying all this, you'll have noticed that you can create a top-level page simply by clicking on the admin area's Pages link in the top menu, and then you're shown an empty Insert Page Details form. It makes sense, though, to also have it available from the pagelist on the left-hand side. So, let's make that add main page button useful. If you remember, we created a pages_add_main_page function in the menu.js file, just as a placeholder until we got everything else done. Open up that file now, /ww.admin/pages/menu.js, and replace that function with the following two new functions: function pages_add_main_page(){ pages_new(0);}function pages_new(p){ $('<form id="newpage_dialog" action="/ww.admin/pages.php" method="post"> <input type="hidden" name="action" value="Insert Page Details" /> <input type="hidden" name="special[1]" value="1" /> <input type="hidden" name="parent" value="'+p+'" /> <table> <tr><th>Name</th><td><input name="name" /></td></tr> <tr><th>Page Type</th><td><select name="type"> <option value="0">normal</option> </select></td></tr> <tr><th>Associated Date</th><td> <input name="associated_date" class="date-human" id="newpage_date" /></td></tr> </table> </form>') .dialog({ modal:true, buttons:{ 'Create Page': function() { $('#newpage_dialog').submit(); }, 'Cancel': function() { $(this).dialog('destroy'); $(this).remove(); } } }); $('#newpage_date').each(convert_date_to_human_readable); return false;} When the add main page button is clicked, a dialog box is created asking some basic information about the page to create: We include a few hidden inputs. action: To tell the server this is an Insert Page Details action. special: When Create Page is clicked, the page will be saved in the database, but we should hide it initially so that front-end readers don't see a half-finished page. So, the special[1] flag is set (21 == 2, which is the value for hiding a page). parent: Note that this is a variable. We can use the same dialog to create sub-pages. When the dialog has been created, the date input box is converted to human-readable. Creating new sub-pages We will add sub-pages by using context menus on the page list. Note that we have a message saying right-click for options under the list. First, add this function to the /ww.admin/pages/menu.js file: function pages_add_subpage(node,tree){ var p=node[0].id.replace(/.*_/,''); pages_new(p);} We will now need to activate the context menu. This is done by adding a contextmenu plugin to the jstree plugin . Luckily, it comes with the download, so you've already installed it. Add it to the page by editing /ww.admin/pages/menu.php and add this highlighted line: <script src="/j/jquery.jstree/jquery.tree.js"></script><script src= "/j/jquery.jstree/plugins/jquery.tree.contextmenu.js"></script><script src="/ww.admin/pages/menu.js"></script> And now, we edit the .tree() call in /ww.admin/menu.js to tell it what to do: $('#pages-wrapper').tree({ callback:{// SKIPPED FOR BREVITY - DO NOT DELETE THESE LINES }, plugins:{ 'contextmenu':{ 'items':{ 'create' : { 'label' : "Create Page", 'icon' : "create", 'visible' : function (NODE, TREE_OBJ) { if(NODE.length != 1) return 0; return TREE_OBJ.check("creatable", NODE); }, 'action':pages_add_subpage, 'separator_after' : true }, 'rename':false, 'remove':false } } }}); By default, the contextmenu has three links: create, rename, and remove. You need to turn off any you're not currently using by setting them to false. Now if you right-click on any page name in the pagelist, you will have a choice to create a sub-page under it. Deleting pages We will add deletions in the same way, using the context menu. Edit the same file, and this time in the contextmenu code, replace the remove: false line with these: 'remove' : { 'label' : "Delete Page", 'icon' : "remove", 'visible' : function (NODE, TREE_OBJ) { if(NODE.length != 1) return 0; return TREE_OBJ.check("deletable", NODE); }, 'action':pages_delete, 'separator_after' : true} And add the pages_delete function to the same file: function pages_delete(node,tree){ if(!confirm( "Are you sure you want to delete this page?"))return; $.getJSON('/ww.admin/pages/delete.php?id=' +node[0].id.replace(/.*_/,''),function(){ document.location=document.location.toString(); }); } One thing to always keep in mind is whenever creating any code that deletes something in your CMS, you must ask the administrator if he/she is sure, just to make sure it wasn't an accidental click. If the administrator confirms that the click was intentional, then it's not your fault if something important was deleted. So, the pages_delete function first checks for this, and then calls the server to remove the file. The page is then refreshed because this may significantly change the page list tree, as we'll see now. Create the /ww.admin/pages/delete.php file: <?phprequire '../admin_libs.php';$id=(int)$_REQUEST['id'];if(!$id)exit;$r=dbRow("SELECT COUNT(id) AS pagecount FROM pages");if($r['pagecount']<2){ die('cannot delete - there must always be one page');}else{ $pid=dbOne("select parent from pages where id=$id",'parent'); dbQuery("delete from pages where id=$id"); dbQuery("update pages set parent=$pid where parent=$id");}echo 1; First, we ensure that there is always at least one page in the database. If deleting this page would empty the table, then we refuse to do it. There is no need to add an alert explaining this, as it should be clear to anyone that deleting the last remaining page in a website leaves the website with no content at all. Simply refusing the deletion should be enough. Next, we delete the page. Finally, any pages which were contained within that page (pages which had this one as their parent), are moved up in the tree so they are contained in the deleted page's old parent. For example, if you had a page, page1>page2>page3 (where > indicates the hierarchy), and you removed page2, then page3 would then be in the position page1>page3. This can cause a large difference in the treestructure if there were quite a few pages contained under the deleted one, so the page needs to be refreshed.
Read more
  • 0
  • 0
  • 887

article-image-getting-started-magento-development
Packt
15 Dec 2010
8 min read
Save for later

Getting Started with Magento Development

Packt
15 Dec 2010
8 min read
Magento is an open-source eCommerce stack with smart features such as layered navigation, auto-complete search, multiple language support, multiple stores, smart browsing, RSS product feeds, tagging and reviewing products, reviewing search terms, reviewing customer tags, poll manager, currency exchange rates, Google sitemap integration, abandoned shopping cart report, catalog-wide product comparisons, product wish lists, and even zooming the product image. It was created by Varien, based on the top notch MVC framework—Zend Framework, on March 31, 2008. As with other MVC applications, Magento keeps the display logic separated from the application's business logic. A dedicated team works on updating Magento regularly. Magento Developer is a very hands-on role for both a novice and experienced software engineer who is interested in achieving high impact in a fast-paced development environment with an ambitious mission. Magento gives a very handy way to deal with its features faster than any other alternatives, whether you know a little or a lot about PHP programming. Let's see how these things happen. We will make our development platform ready to cook some mouth-watering recipes for Magento in this article. If you are a newbie, this is the right place to start. If you are a pro, this is still the right place to start as we have tried to follow some best practices for Magento development that you might not be aware of. Let's get our hands dirty with Magento Development! Good luck! Preparing the platform with a virtual host Magento is built on the de facto PHP framework—Zend Framework. We need to set up our development environment properly to get most out of it. In this recipe, we will set up a Fully Qualifed Domain Name (FQDN) and a virtual host. We could use the domain as http://localhost/magento or something like this, but in that case accessing the admin panel might be cumbersome. In most cases, you have to access through the local IP address. Using a FQDN, you don't have to worry about it. A FQDN will make the debugging process much easier also. Getting Ready We need to have the following things installed before kicking off the setup of a virtual host a.k.a. vhost: Apache2 PHP5 MySQL server 5.0 If we want to install the previously mentioned tools from Ubuntu Linux CLI, we can easily do that by running the following commands. We will be using Linux commands based on Ubuntu. The following command is a basic Apache installation command: sudo aptitude install apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert The following command is a basic PHP5 installation command: sudo aptitude install libapache2-mod-php5 php5 php5-common php5-curl php5-dev php5-gd php5-imagick php5-mcrypt php5-memcache php5-mhash php5- mysql php5-pspell php5-snmp php5-sqlite php5-xmlrpc php5-xsl Enter the following command to begin with a simple MySQL installation: sudo aptitude install mysql-server mysql-client libmysqlclient15-dev Note that we have installed the development libs and headers with the libmysqlclient15-dev package. You can leave that out but it has been found that they are useful in many situations. Alternately, we can use an all-in-one package, such as the XAMPP, to get all the aforementioned tools in one click. The XAMPP package can be downloaded from http://www.apachefriends.org/en/xampp.html. How to do it... To test the domain without creating a DNS zone and record(s) on some Internet nameserver(s), let's modify the /etc/hosts file on our local computer to include some entries mapping the magento.local.com, and so on to the local machine's public IP address (in most cases, 127.0.0.1). Open the /etc/hosts file with any text editor or run the following command in terminal: sudo nano /etc/hosts Add the following line as a similar entry in /etc/hosts file: 127.0.0.1 magento.local.com The location of the host file depends on the OS loaded. If it's a Windows OS it should be c:windowssystem32driversetchosts. Now let's create the layout for our domain. Open terminal and change your location to the WEBROOT directory by running the following command: cd /var/www/ If you are using XAMPP, it would be c:xampphtdocs. Now for our domain, we want to host/create a folder with a standard set of subfolders: mkdir -p magento.local.com/{public,private,log,cgi-bin,backup} Let's create an index.html file for our domain: sudo nano /var/www/magento.local.com/public/index.html It's time to put some content in our index file: <html> <head> <title>Welcome to magento.local.com</title> </head> <body> <h1>Welcome to magento.local.com</h1> </body> </html> We've set up the basics and now we're ready to add our own virtual hosts, so that we can start to serve our domain. Let's go ahead and create the vhost file for magento.local.com: sudo nano /etc/apache2/sites-available/magento.local.com The contents should look like this: # Place any notes or comments you have here # It will make any customization easier to understand in the weeks to come # domain: magento.local.com # public: /var/www/magento.local.com/public <VirtualHost *:80> # Admin email, Server Name (domain name) and any aliases ServerAdmin [email protected] ServerName magento.local.com # Index file and Document Root (where the public files are located) DirectoryIndex index.php index.html DocumentRoot /var/www/magento.local.com/public # Custom log file locations LogLevel warn ErrorLog /var/www/magento.local.com/log/error.log CustomLog /var/www/magento.local.com/log/access.log combined </VirtualHost> Now we have the site available, we need to enable it: sudo a2ensite magento.local.com It seems like good advice to reload the apache: sudo /etc/init.d/apache2 reload With such changes made, we can now navigate to our site in a web browser on our local computer, as seen in the following screenshot: Tada! We now have the contents of public/index.html being shown. How it works... The Getting ready recipe describes the installation process of Apache, PHP, and MySQL from Linux command line. If you have already installed those, you can skip it and start confguring the layout and vhost directly. The Magento source files will be put in the public directory. Other directories represent as their name suggest. Usually in production server, we don't have to write the domain name manually as we wrote here, as the DNS zone is there. The rest of this recipe is the same as the production server. The virtual host contents has some inline comments starting with # to describe the purpose. Setting up a Subversion/SVN A Subversion is a popular version control system initiated in 2000 by CollabNet Inc. Its goal is to be a mostly-compatible successor to the widely used Concurrent Versions System (CVS). Getting Ready If you are a Windows user, please download the TortoiseSVN client from here: http://tortoisesvn.tigris.org/ and if you are a Linux user, fre up your terminal. How to do it... Execute the following command in the terminal to install Subversion: sudo apt-get install subversion Make sure you have an active Internet connection as aptitude will install the package from Ubuntu repository. If you are using windows and intended to use TortoiseSVN you may get a Windows binary from http://tortoisesvn.tigris.org. After downloading the TortoiseSVN installer file, double-click on the TortoiseSVN installer file and follow the on screen instructions. Now let's add a new group called Subversion to our system and add current username to Subversion group. sudo addgroup subversion sudo adduser <your_username_here> subversion If you don't a have a clue about your current username, issue the command whoami in your terminal. Once the Subversion installation is completed, let's create our repository for Magento project. Now issue the following command in the terminal: sudo mkdir /home/svn cd /home/svn sudo mkdir magento sudo chown -R www-data:subversion magento sudo chmod -R g+rws magento Now initiate an SVN repository with the following command: sudo svnadmin create /home/svn/magento In the case of TortoiseSVN, open the folder where you want to put your Magento repository and right-click and find create repository here... from the context menu TortoiseSVN. Enter a name for your repository, in this case the repository name would be magento. It's time to change your current working directory in terminal to: cd /var/www/magento.local.com We will checkout the Magento project by issuing the following command in the terminal: svn co file:///home/svn/magento public How it works... When we execute any apt-get command, the aptitude will take care of the installation process. The aptitude will look for the package from Ubuntu central repository—and download it if necessary—and perform the necessary tasks. After completion of the installation process, we've created a new group named subversion and added the username to the newly created group subversion. Then we've created a directory under /home/location to hold all repositories. After that, we've created a new repository for our Magento project named magento. Finally, we've checked out the repository in our site location in the public directory.
Read more
  • 0
  • 0
  • 1867
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-drupal-7-module-development-drupals-theme-layer
Packt
14 Dec 2010
16 min read
Save for later

Drupal 7 Module Development: Drupal's Theme Layer

Packt
14 Dec 2010
16 min read
  Drupal 7 Module Development Create your own Drupal 7 modules from scratch Specifically written for Drupal 7 development Write your own Drupal modules, themes, and libraries Discover the powerful new tools introduced in Drupal 7 Learn the programming secrets of six experienced Drupal developers Get practical with this book's project-based format Business logic versus presentation logic So what would be the best way to get our data and functionality marked up? Do we simply wrap each piece of data in HTML and return the whole as a giant string? Like the following example: return '<div class="wrapper">' . $data . '</div>'; Fortunately, we don't. Like all other well-designed applications, Drupal separates its business logic from its presentation logic. Traditionally, the primary motivations for this separation of concerns are as follows: To make the code easier to maintain. To make it possible to easily swap out one layer's implementation without having to re-write the other layers. As we shall see, Drupal takes the "swap-ability" aspect to the extreme. As we mentioned in the introduction of this article, the default theme selected on the Appearance page is the most obvious part of the theme layer. Also, you might think that the theme is responsible for applying the HTML and CSS for the website. However, there are thousands of contributed modules on drupal.org. Should the theme be responsible for marking up all of those modules' data? Obviously not. Since a module is most intimately familiar with its own data and functionality, it's the module's responsibility to provide the default theme implementation. As long as the module uses the theme system properly, a theme will be able to override any HTML and CSS by hot-swapping its own implementation for the module's implementation. After the data has been retrieved and manipulated in the heart of your module (the business logic), it will need to provide the default theme implementation. Sometimes a particular theme will need to override your implementation in order for it to achieve a specifc design goal; if the theme provides its own implementation, Drupal will use the theme implementation instead of the module's default implementation. $variables = array('items' => $list, 'type' => 'ol'); $content = theme('item_list', $variables); By calling the theme() function, we are delegating the responsibility of determining and using the proper theme implementation. We're saying: "Hey, theme()! I want to markup my data as an item_list. Can you do that for me? I don't need to know the details. kthxbye." Our module just needs to decide which theme hook it wants to use to markup its data. Should the data be displayed in an unordered list, a table, or a wordle? Hook crazy? In addition to API hooks, Drupal also has theme hooks. A theme hook is simply the name of a particular way to markup some data. For example, passing data to the item_list theme hook will result in different markup then passing data to the links theme hook. However, while normally every module's hook function will be called when Drupal invokes an API hook, only one theme hook implementation will be invoked when Drupal invokes a theme hook. There are actually two different ways you can make an implementation (which we will discuss later), but for now we'll only talk about the simplest method for module developers—theme functions. When you call theme(), it will look for a default theme function named theme_HOOKNAME and for an optional theme override function called THEMENAME_HOOKNAME. If you dig into Drupal's internals, you'll fnd a theme_item_list() inside includes.inc or theme.inc. This is Drupal's default theme implementation for an item_list. If our active theme was Bartik, and if Bartik implemented a theme override called bartik_item_list(), then theme() would use the Bartik theme's implementation instead of the default one. The preceding fgure shows one piece of data as it passes through a module and a theme. However, in order for you to understand the full power of Drupal's theme layer, you also need to understand how the entire page is built. However, since all of the active theme's modifcations occur after any module modifcations, from a module developer's perspective, all of this theme inheritance is transparent. Since modules don't need to know anything about the structure o the theme and its ancestry, we'll simply talk about "the theme" in this book. Just be aware that the actual theme may be more complex. Base themes and sub-themes If you've previously read anything about Drupal theming, you've probably heard about base themes and sub-themes. Any theme can declare a parent theme in its .info file using the base theme key and it will inherit all the hook implementations from its parent theme (and its parent's parent theme, and so on). Data granularity One of the things that makes Drupal theming so powerful is its granularity. Each piece of content is handled separately as it's passed through the theming system. Each bit of data is themed individually, then combined into ever-larger chunks. At each step in the aggregation process, it's themed again. The following illustration will make this clearer: As you can see in the preceding illustration, for a typical blog post, each comment is pulled from the database and sent through the theme system to get HTML markup added to it. Then all the comments are aggregated together into a "comment wrapper" where additional markup and, usually, a "new comment" form is added. Then the single group of comments is passed to the node theming where it is combined with other pieces of the blog post's content. This process of theming bits of content, aggregation, and theming again is repeated until we've built the entire HTML page ready to be sent to a web browser. There are two advantages to this granular system. First, since each module is responsible for theming its own data, it can either create a very specialized theme hook for its data or it can re-use an existing theme hook. Re-using a theme hook ensures a consistent set of markup for similar data structures while still allowing customized CSS classes (Most theme hooks allow custom classes to be passed as parameters.) For example, the list of links after a node (read more, add new comment, and so on) re-uses the links theme hook, and the links after each comment use the same links theme hook. The second advantage is for the theme developer. Having a fine-grained theming system means that a theme, if it chooses to, can literally rewrite all of the markup for its own design purposes. As module developers we need to be keenly aware of the themer's desire to have granular theming overrides.   Theme engines Some themes require alternate theme engines. Theme engines can provide alternate template syntax, naming standards, and helper functions. Several theme engines are available for download at http://drupal.org/project/theme+engines. However, we won't be discussing any theme engines except for Drupal's default theme engine, PHPTemplate. The PHPTemplate theme engine has been the default theme since Drupal 4.7, has been continuously improved with each version, and has proven its worth again and again. Over 99% of themes available for download on drupal.org use the default PHPTemplate theme engine. Two ways to theme So now that we have a good understanding of higher level concepts, let's get down to the nitty-gritty of theme implementations. There are actually two different ways to implement a theme hook: Theme functions: pass data to a PHP function to wrap it in markup Templates: pass data to a template which is a PHP file mixed with markup and PHP print statements Let's look at each of these in turn. Theme functions For a module developer, the easiest type of implementation to understand is a theme function. Theme functions just need to follow a few simple rules in order for them to work properly. First, the name of the theme function follows the pattern: theme_[theme hook name] Since the theme hook name is used directly in the theme function's name, theme hook names have the same constraints on naming as regular PHP function names; the only valid characters in theme hook names are alphanumeric characters and underscores. So if a module has created an example_format theme hook, it would implement it with theme function named theme_example_format(). Second, the theme function will only have a single parameter, as follows: function theme_THEME_HOOK_NAME($variables) {…} The theme function variables are an associative array containing the pieces of data we wish to markup and any options we want to pass to the function. It may seem extremely odd not to use multiple parameters and PHP's ability to specify default values for each parameter. In fact, previous versions of Drupal did use multiple parameters. We'll see why Drupal now only uses one parameter in just a moment when we talk about preprocess functions. For an example of a $variables array, let's look at how the DocBlock of the theme_item_list() function defnes it: Items: An array of items to be displayed in the list. If an item is a string, then it is used as is. If an item is an array, then the data element of the array is used as the contents of the list item. If an item is an array with a "children" element, those children are displayed in a nested list. All other elements are treated as attributes of the list item element. Title: The title of the list. Type: The type of list to return (e.g. ul,ol). Attributes: The attributes applied to the list element. The items and title keys hold the actual data, and the type and attributes keys are options that specify how to build the item list. Third, the theme function should return a string that contains the rendered representation of the data. This is usually a string of HTML, but some theme hooks return other types of themed markup. For example, theme_syslog_format returns a simple string with pipe-separated data values for use in a *NIX syslog error log. That's it! As you can see, theme functions have very simple requirements and in every other way are standard PHP functions. The major difference between most functions and theme functions is that you should never call theme functions directly. It may be tempting to take your data and call theme_item_list($vars) directly, but you should instead call theme("item_list", $vars). This method of calling theme functions indirectly ensures that themes are able to override any module's default theme function (or template). It also allows the theme() function to work additional magic, including allowing other modules to alter the theme function's variables before they are used. Preprocess functions Now we're starting to see the real flexibility of the theme system. Preprocess functions allow one module to alter the variables used by another module when it calls a theme hook. So if some code passes data to theme() for a particular theme hook, preprocess functions will be called to alter the data before the actual theme hook implementation is called. The following steps are carried out: Code calls theme('hook_name', $variables). theme() calls preprocess functions for hook_name. Preprocess functions modify variables. theme() calls actual implementation for hook_name with modifed variables. All preprocess functions take the form of: [module]_preprocess_[theme hook name](&$variables) So if the foo module wants to alter the variables for the item_list theme hook, it could define the function as follows: function foo_preprocess_item_list(&$variables) { // Add a class to the list wrapper. $variables['attributes']['class'][] = 'foo-list'; } Notice that the $variables parameter is defned with an ampersand in front of it. That's PHP notation to pass the parameter by reference. Instead of getting a copy of the variables, the foo_preprocess_item_list() function will get access to the actual $variables which is later passed to the theme function implementation. So any modifications that the preprocess function makes to the $variables parameter will be preserved when those variables are passed to the theme function. That's the reason our example foo_preprocess_item_list() function doesn't return anything; its work is done directly on the original $variables. This is extremely handy for module developers as it allows all sorts of integration with other modules. Since the variables parameter is a mix of data and options, modules can alter both the raw data and change the way data will be rendered. This can be as simple as one module needing a special class for use in its JavaScript code and adding that class to another module's themed content by appending to the $variables['attributes']['class'] array, or can be more complex interactions like the i18n module translating the language used in blocks. Imagine we've built a retro module that integrates GeoCities and we want to replace all links to a user's profle page with a link to the user's GeoCities homepage. We can do that relatively easily with a preprocess function. First let's look at the following theme_username function's documentation: /** * Format a username. * * @param $variables * An associative array containing: * - account: The user object to format. * - name: The user's name, sanitized. * - extra: Additional text to append to the user's name, sanitized. * - link_path: The path or URL of the user's profile page, home * page, or other desired page to link to for more information * about the user. * - link_options: An array of options to pass to the l() function's * $options parameter if linking the user's name to the user's * page. * - attributes_array: An array of attributes to pass to the * drupal_attributes() function if not linking to the user's page. */ Quite conveniently, theme_username() has a handy $link_path variable that we want to alter to achieve our old-school giggles. Assuming that we've used some other business logic with the user module's hooks to load our GeoCities URL into the user's account (the "hard" part), replacing the link to the user's profle page can be accomplished with the following simple preprocess function: /** * Implements awesomeness with hook_preprocess_username(). */ function retro_preprocess_username(&$variables) { $variables['link_path'] = $variables['account']->geocities_url; } That's it! We don't have to override the user module's theme implementation; we just modify its parameters. Theme overrides While module developers usually don't have to worry about whether a theme overrides a particular theme function or not, it's still important to understand how this mechanism works. Drupal theme is normally composed of CSS, images, JavaScripts, template files (discussed shortly), a .info file, and a template.php file. The template.php file is analogous to a module's .module file. It contains all of the PHP functions for the theme and is automatically loaded when the theme is initialized. If a theme wants to override a particular theme function, it needs to copy the theme function from its original location and paste it into its template.php file. Then it needs to change the function's prefix from theme to its own name and finally, it needs to start making the desired changes to the function. For example, if the Bartik theme wants to override the theme_menu_local_tasks() function in order to add some markup around the page's tabs, it would copy the entire function from includes/menu.inc, paste it into Bartik's template.php, and rename it to bartik_menu_local_tasks(). Fortunately, when a theme overrides a default theme function, a module's preprocess functions continue to work as normal. Themes also have the ability to create preprocess functions. If the Bartik theme decides to format a user's name in "last name, frst name" format, it can implement a bartik_preprocess_username() function. Fortunately, a theme's preprocess functions do not override a module's preprocess functions. All preprocess functions are run; frst any module's preprocess functions and then the theme's preprocess function. Template files While theme functions might be the easiest for module developers to understand, template files are the easiest for themes to grasp. When a theme hook is implemented with template files, they are used instead of theme functions. However, from a module developer's standpoint, there is actually a remarkable amount of similarity between template files and theme functions. First, let's take a closer look at template files. Templates are files primarily containing HTML but with some PHP statements mixed in using the template's variables. Instead of declaring a theme_hook_name() function, a module would instead create a hook-name.tpl.php file. The following are the contents of a typical template file, typical-hook.tpl.php: <div class="<?php print $classes; ?>"<?php print $attributes; ?>> <?php if ($title): ?> <h2<?php print $title_attributes; ?>> <?php print $title; ?> </h2> <?php endif;?> <div class="submitted"> <?php print t('By !author @time ago', array( '@time' => $time, '!author' => $author, )); ?> </div> <div class="content"<?php print $content_attributes; ?>> <?php // We hide the links now so that we can render them later. hide($content['links']); print render($content); ?> </div> <?php print render($content['links']); ?> </div> The preceding example shows the full gamut of the things that you are likely see in a template file. They are as follows: Printing a variable containing a string? Printing a translatable string using t() Conditional if/else/endif statement Delaying rendering on part of a render element with hide() Printing a render element All of the PHP in a template should be limited to printing out variables. This limited amount of PHP makes it much easier for non-programmers to learn how to use template fles compared to theme functions. However, for module developers, the template implementation is still very similar to the theme function implementation; the handful of differences are relatively minor. As with theme function implementations, our module would still need to invoke the theme hook using theme(). $variables = array('typical' => $typical_object); $output = theme('typical_hook', $variables); The theme() function would discover that the typical_hook theme hook was implemented as a template and render the corresponding typical-hook.tpl.php file. The only valid characters in theme hook names are alphanumeric characters and underscores. This is true of all theme hooks, regardless of whether they are implemented as a theme function or as a template file. However, when theme() looks for template implementations, it will automatically convert any underscores in the theme hook name into hyphens while searching for the template file. For example, calling theme('user_picture', $variables) will result in the template file named user-picture.tpl.php being rendered. Also, just like theme functions, other modules can modify the variables using preprocess functions. In template fles the focus is on printing out variables in various places in the markup. So for template fles, the preprocess function takes on a more important role. The only difference between a theme function's preprocess functions and a template file's are the number and type of preprocess functions.
Read more
  • 0
  • 0
  • 3073

article-image-page-management-part-one-cms-design
Packt
14 Dec 2010
6 min read
Save for later

Page Management - Part One in CMS Design

Packt
14 Dec 2010
6 min read
  CMS Design Using PHP and jQuery Build and improve your in-house PHP CMS by enhancing it with jQuery Create a completely functional and a professional looking CMS Add a modular architecture to your CMS and create template-driven web designs Use jQuery plugins to enhance the "feel" of your CMS A step-by-step explanatory tutorial to get your hands dirty in building your own CMS         Read more about this book       (For more resources on this subject, see here.) How pages work in a CMS A "page" is simply the main content which should be shown when a certain URL is requested. In a non-CMS website, this is easy to see, as a single URL returns a distinct HTML file. In a CMS though, the page is generated dynamically, and may include features such as plugins, different views depending on whether the reader was searching for something, whether pagination is used, and other little complexities. In most websites, a page is easily identified as the large content area in the middle (this is an over-simplification). In others, it's harder to tell, as the onscreen page may be composed of content snippets from other parts of the site. We handle these differences by using page "types", each of which can be rendered differently on the frontend. Examples of types include gallery pages, forms, news contents, search results, and so on. In this article, we will create the simplest type, which we will call "normal". This consists of a content-entry textarea in the admin area, and direct output of that content on the front-end. You could call this "default" if you want, but since a CMS is not always used by people from a technical background, it makes sense to use a word that they are more likely to recognize. I have been asked before by clients what "default" means, but I've never been asked what "normal" means. At the very least, a CMS needs some way to create the simplest of web pages. This is why the "normal" type is not a plugin, but is built into the core. Listing pages in the admin area To begin, we will add Pages to the admin menu. Edit /ww.admin/header.php and add the following highlighted line: <ul> <li><a href="/ww.admin/pages.php">Pages</a></li> <li><a href="/ww.admin/users.php">Users</a></li> And one more thing—when we log into the administration part of the CMS, it makes sense to have the "front page" of the admin area be the Pages section. After all, most of the work in a CMS is done in the Pages section. So, we change /ww.admin/index.php so it is a synonym for /ww.admin/pages.php. Replace the /ww.admin/index.php file with this: <?php require 'pages.php'; Next, let's get started on the Pages section. First, we will create /ww.admin/pages.php: <?php require 'header.php'; echo '<h1>Pages</h1>'; // { load menu echo '<div class="left-menu">'; require 'pages/menu.php'; echo '</div>'; // } // { load main page echo '<div class="has-left-menu">'; require 'pages/forms.php'; echo '</div>'; // } echo '<style type="text/css"> @import "pages/css.css";</style>'; require 'footer.php'; Notice how I've commented blocks of code, using // { to open the comment at the beginning of the block, and // } at the end of the block. This is done because a number of text editors have a feature called "folding", which allows blocks enclosed within delimiters such as { and } to be hidden from view, with just the first line showing. For instance, the previous code example looks like this in my Vim editor: What the page.php does is to load the headers, load the menu and page form, and then load the footers. For now, create the directory /ww.admin/pages and create a file in it called /ww.admin/pages/forms.php: <h2>FORM GOES HERE</h2> And now we can create the page menu. Use the following code to create the file /ww.admin/pages/menu.php: <?php echo '<div id="pages-wrapper">'; $rs=dbAll('select id,type,name,parent from pages order by ord,name'); $pages=array(); foreach($rs as $r){ if(!isset($pages[$r['parent']]))$pages[$r['parent']]=array(); $pages[$r['parent']][]=$r; } function show_pages($id,$pages){ if(!isset($pages[$id]))return; echo '<ul>'; foreach($pages[$id] as $page){ echo '<li id="page_'.$page['id'].'">' .'<a href="pages.php?id='.$page['id'].'"'>' .'<ins>&nbsp;</ins>'.htmlspecialchars($page['name']) .'</a>'; show_pages($page['id'],$pages); echo '</li>'; } echo '</ul>'; } show_pages(0,$pages); echo '</div>'; That will build up a &ltul> tree of pages. Note the use of the "parent" field in there. Most websites follow a hierarchical "parent-child" method of arranging pages, with all pages being a child of either another page, or the "root" of the site. The parent field is filled with the ID of the page within which it is situated. There are two main ways to indicate which page is the "front" page (that is, what page is shown when someone loads up http://cms/ with no page name indicated). You can have one single page in the database which has a parent of 0, meaning that it has no parent—this page is what is looked for when http://cms/ is called. In this scheme, pages such as http://cms/pagename have their parent field set to the ID of the one page which has a parent of 0. You can have many pages which have 0 as their parent, and each of these is said to be a "top-level" page. One page in the database has a flag set in the special field which indicates that this is the front page. In this scheme, pages named like http://cms/pagename all have a parent of 0, and the page corresponding to http://cms/ can be located anywhere at all in the database. Case 1 has a disadvantage, in that if you want to change what page is the front page, you need to move the current page under another one (or delete it), then move all the current page's child-pages so they have the new front page's ID as a parent, and this can get messy if the new front-page already had some sub-pages—especially if there are any with equal names. Case 2 is a much better choice because you can change the front page whenever you want, and it doesn't cause any problems at all. When you view the site in your browser now, it looks like this:
Read more
  • 0
  • 0
  • 1994

article-image-creating-your-first-module-drupal-7-module-development
Packt
14 Dec 2010
15 min read
Save for later

Using Drupal 7 for Module Development

Packt
14 Dec 2010
15 min read
  Drupal 7 Module Development Create your own Drupal 7 modules from scratch Specifically written for Drupal 7 development Write your own Drupal modules, themes, and libraries Discover the powerful new tools introduced in Drupal 7 Learn the programming secrets of six experienced Drupal developers Get practical with this book's project-based format         Read more about this book       The focus of this article by Matt Butcher, author of Drupal 7 Module Development, is module creation. We are going to begin coding in this article. Here are some of the important topics that we will cover in this article: Starting a new module Creating .info files to provide Drupal with module information Creating .module files to store Drupal code Adding new blocks using the Block Subsystem Using common Drupal functions Formatting code according to the Drupal coding standards (For more resources on this subject, see here.) Our goal: a module with a block In this article we are going to build a simple module. The module will use the Block Subsystem to add a new custom block. The block that we add will simply display a list of all of the currently enabled modules on our Drupal installation. We are going to divide this task of building a new module into the three parts: Create a new module folder and module files Work with the Block Subsystem Write automated tests using the SimpleTest framework included in Drupal We are going to proceed in that order for the sake of simplicity. One might object that, following agile development processes, we ought to begin by writing our tests. This approach is called Test-driven Development (TDD), and is a justly popular methodology. Agile software development is a particular methodology designed to help teams of developers effectively and efficiently build software. While Drupal itself has not been developed using an agile process, it does facilitate many of the agile practices. To learn more about agile, visit http://agilemanifesto.org/ However, our goal here is not to exemplify a particular methodology, but to discover how to write modules. It is easier to learn module development by first writing the module, and then learn how to write unit tests. It is easier for two reasons: SimpleTest (in spite of its name) is the least simple part of this article. It will have double the code-weight of our actual module. We will need to become acquainted with the APIs we are going to use in development before we attempt to write tests that assume knowledge of those APIs. In regular module development, though, you may certainly choose to follow the TDD approach of writing tests first, and then writing the module. Let's now move on to the first step of creating a new module. Creating a new module Creating Drupal modules is easy. How easy? Easy enough that over 5,000 modules have been developed, and many Drupal developers are even PHP novices! In fact, the code in this article is an illustration of how easy module coding can be. We are going to create our first module with only one directory and two small files. Module names It goes without saying that building a new module requires naming the module. However, there is one minor ambiguity that ought to be cleared up at the outset, a Drupal module has two names: A human-readable name: This name is designed to be read by humans, and should be one or a couple of words long. The words should be capitalized and separated by spaces. For example, one of the most popular Drupal modules has the human-readable name Views. A less-popular (but perhaps more creatively named) Drupal 6 module has the human-readable name Eldorado Superfly. A machine-readable name: This name is used internally by Drupal. It can be composed of lower-case and upper-case letters, digits, and the underscore character (using upper-case letters in machine names is frowned upon, though). No other characters are allowed. The machine names of the above two modules are views and eldorado_superfly, respectively. By convention, the two names ought to be as similar as possible. Spaces should be replaced by underscores. Upper-case letters should generally be changed to lower-case. Because of the convention of similar naming, the two names can usually be used interchangeably, and most of the time it is not necessary to specifically declare which of the two names we are referring to. In cases where the difference needs to be made (as in the next section), the authors will be careful to make it. Where does our module go? One of the less intuitive aspects of Drupal development is the filesystem layout. Where do we put a new module? The obvious answer would be to put it in the /modules directory alongside all of the core modules. As obvious as this may seem, the /modules folder is not the right place for your modules. In fact, you should never change anything in that directory. It is reserved for core Drupal modules only, and will be overwritten during upgrades. The second, far less obvious place to put modules is in /sites/all/modules. This is the location where all unmodified add-on modules ought to go, and tools like Drush ( a Drupal command line tool) will download modules to this directory. In some sense, it is okay to put modules here. They will not be automatically overwritten during core upgrades. However, as of this writing, /sites/all/modules is not the recommended place to put custom modules unless you are running a multi-site configuration and the custom module needs to be accessible on all sites. The current recommendation is to put custom modules in the /sites/default/modules directory, which does not exist by default. This has a few advantages. One is that standard add-on modules are stored elsewhere, and this separation makes it easier for us to find our own code without sorting through clutter. There are other benefits (such as the loading order of module directories), but none will have a direct impact on us. We will always be putting our custom modules in /sites/default/modules. This follows Drupal best practices, and also makes it easy to find our modules as opposed to all of the other add-on modules. The one disadvantage of storing all custom modules in /sites/default/modules appears only under a specific set of circumstances. If you have Drupal configured to serve multiple sites off of one single instance, then the /sites/default folder is only used for the default site. What this means, in practice, is that modules stored there will not be loaded at all for other sites. In such cases, it is generally advised to move your custom modules into /sites/all/modules/custom. Other module directories Drupal does look in a few other places for modules. However, those places are reserved for special purposes. Creating the module directory Now that we know that our modules should go in /sites/default/modules, we can create a new module there. Modules can be organized in a variety of ways, but the best practice is to create a module directory in /sites/default/modules, and then place at least two files inside the directory: a .info (pronounced "dot-info") file and a .module ("dot-module") file. The directory should be named with the machine-readable name of the module. Similarly, both the .info and .module files should use the machine-readable name. We are going to name our first module with the machine-readable name first, since it is our first module. Thus, we will create a new directory, /sites/default/modules/first, and then create a first.info file and a first.module file: Those are the only files we will need for our module. For permissions, make sure that your webserver can read both the .info and .module files. It should not be able to write to either file, though. In some sense, the only file absolutely necessary for a module is the .info file located at a proper place in the system. However, since the .info file simply provides information about the module, no interesting module can be built with just this file. Next, we will write the contents of the .info file. Writing the .info file The purpose of the .info file is to provide Drupal with information about a module—information such as the human-readable name, what other modules this module requires, and what code files this module provides. A .info file is a plain text file in a format similar to the standard INI configuration file. A directive in the .info file is composed of a name, and equal sign, and a value: name = value By Drupal's coding conventions, there should always be one space on each side of the equals sign. Some directives use an array-like syntax to declare that one name has multiple values. The array-like format looks like this: name[] = value1 name[] = value2 Note that there is no blank space between the opening square bracket and the closing square bracket. If a value spans more than one line, it should be enclosed in quotation marks. Any line that begins with a ; (semi-colon) is treated as a comment, and is ignored by the Drupal INI parser. Drupal does not support INI-style section headers such as those found in the php.ini file. To begin, let's take a look at a complete first.info file for our first module: ;$Id$ name = First description = A first module. package = Drupal 7 Development core = 7.x files[] = first.module ;dependencies[] = autoload ;php = 5.2 This ten-line file is about as complex as a module's .info file ever gets. The first line is a standard. Every .info file should begin with ;$Id$. What is this? It is the placeholder for the version control system to store information about the file. When the file is checked into Drupal's CVS repository, the line will be automatically expanded to something like this: ;$Id: first.info,v 1.1 2009/03/18 20:27:12 mbutcher Exp $ This information indicates when the file was last checked into CVS, and who checked it in. CVS is going away, and so is $Id$. While Drupal has been developed in CVS from the early days through Drupal 7, it is now being migrated to a Git repository. Git does not use $Id$, so it is likely that between the release of Drupal 7 and the release of Drupal 8, $Id$ tags will be removed. You will see all PHP and .info files beginning with the $Id$ marker. Once Drupal uses Git, those tags may go away. The next couple of lines of interest in first.info are these: name = First description = A first module. package = Drupal 7 Development The first two are required in every .info file. The name directive is used to declare what the module's human-readable name is. The description provides a one or two-sentence description of what this module provides or is used for. Among other places, this information is displayed on the module configuration section of the administration interface in Modules. In the screenshot, the values of the name and description fields are displayed in their respective columns. The third item, package, identifies which family (package) of modules this module is related to. Core modules, for example, all have the package Core. In the screenshot above, you can see the grouping package Core in the upper-left corner. Our module will be grouped under the package Drupal 7 Development to represent its relationship. As you may notice, package names are written as human-readable values. When choosing a human-readable module name, remember to adhere to the specifications mentioned earlier in this section. The next directive is the core directive: core = 7.x. This simply declares which main-line version of Drupal is required by the module. All Drupal 7 modules will have the line core = 7.x. Along with the core version, a .info file can also specify what version of PHP it requires. By default, Drupal 7 requires Drupal 5.1 or newer. However, if one were to use, say, closures (a feature introduced in PHP 5.3), then the following line would need to be added: php = 5.3 Next, every .info file must declare which files in the module contain PHP functions, classes, or interfaces. This is done using the files[] directive. Our small initial module will only have one file, first.module. So we need only one files[] directive. files[] = first.module More complex files will often have several files[] directives, each declaring a separate PHP source code file. JavaScript, CSS, image files, and PHP files (like templates) that do not contain functions that the module needs to know about needn't be included in files[] directives. The point of the directive is simply to indicate to Drupal that these files should be examined by Drupal. One directive that we will not use for this module, but which plays a very important role is the dependencies[] directive. This is used to list the other modules that must be installed and active for this module to function correctly. Drupal will not allow a module to be enabled unless its dependencies have been satisfied. Drupal does not contain a directive to indicate that another module is recommended or is optional. It is the task of the developer to appropriately document this fact and make it known. There is currently no recommended best practice to provide such information. Now we have created our first.info file. As soon as Drupal reads this file, the module will appear on our Modules page. In the screenshot, notice that the module appears in the DRUPAL 7 DEVELOPMENT package, and has the NAME and DESCRIPTION as assigned in the .info file. With our .info file completed, we can now move on and code our .module file. Modules checked into Drupal's version control system will automatically have a version directive added to the .info file. This should typically not be altered. Creating a module file The .module file is a PHP file that conventionally contains all of the major hook implementations for a module. We will gain some practical knowledge of them. A hook implementation is a function that follows a certain naming pattern in order to indicate to Drupal that it should be used as a callback for a particular event in the Drupal system. For Object-oriented programmers, it may be helpful to think of a hook as similar to the Observer design pattern. When Drupal encounters an event for which there is a hook (and there are hundreds of such events), Drupal will look through all of the modules for matching hook implementations. It will then execute each hook implementation, one after another. Once all hook implementations have been executed, Drupal will continue its processing. In the past, all Drupal hook implementations had to reside in the .module file. Drupal 7's requirements are more lenient, but in most moderately sized modules, it is still preferable to store most hook implementations in the .module file. There are cases where hook implementations belong in other files. In such cases, the reasons for organizing the module in such a way will be explained. To begin, we will create a simple .module file that contains a single hook implementation – one that provides help information. <?php // $Id$ /** * @file * A module exemplifying Drupal coding practices and APIs. * * This module provides a block that lists all of the * installed modules. It illustrates coding standards, * practices, and API use for Drupal 7. */ /** * Implements hook_help(). */ function first_help($path, $arg) { if ($path == 'admin/help#first') { return t('A demonstration module.'); } } Before we get to the code itself, we will talk about a few stylistic items. To begin, notice that this file, like the .info file, contains an $Id$ marker that CVS will replace when the file is checked in. All PHP files should have this marker following a double-slash-style comment: // $Id$. Next, the preceding code illustrates a few of the important coding standards for Drupal. Source code standards Drupal has a thorough and strictly enforced set of coding standards. All core code adheres to these standards. Most add-on modules do, too. (Those that don't generally receive bug reports for not conforming.) Before you begin coding, it is a good idea to familiarize yourself with the standards as documented here: http://drupal.org/coding-standards. The Coder module can evaluate your code and alert you to any infringement upon the coding standards. We will adhere to the Drupal coding standards. In many cases, we will explain the standards as we go along. Still, the definitive source for standards is the URL listed above, not our code here. We will not re-iterate the coding standards. The details can be found online. However, several prominent standards deserve immediate mention. I will just mention them here, and we will see examples in action as we work through the code. Indenting: All PHP and JavaScript files use two spaces to indent. Tabs are never used for code formatting. The <?php ?> processor instruction: Files that are completely PHP should begin with <?php, but should omit the closing ?>. This is done for several reasons, most notably to prevent the inclusion of whitespace from breaking HTTP headers. Comments: Drupal uses Doxygen-style (/** */) doc-blocks to comment functions, classes, interfaces, constants, files, and globals. All other comments should use the double-slash (//) comment. The pound sign (#) should not be used for commenting. Spaces around operators: Most operators should have a whitespace character on each side. Spacing in control structures: Control structures should have spaces after the name and before the curly brace. The bodies of all control structures should be surrounded by curly braces, and even that of if statements with one-line bodies. Functions: Functions should be named in lowercase letters using underscores to separate words. Later we will see how class method names differ from this. Variables: Variable names should be in all lowercase letters using underscores to separate words. Member variables in objects are named differently. As we work through examples, we will see these and other standards in action. As we work through examples, we will see these and other standards in action.
Read more
  • 0
  • 0
  • 5707

article-image-plugins-cms-design
Packt
10 Dec 2010
15 min read
Save for later

Plugins in CMS Design

Packt
10 Dec 2010
15 min read
  CMS Design Using PHP and jQuery Build and improve your in-house PHP CMS by enhancing it with jQuery Create a completely functional and a professional looking CMS Add a modular architecture to your CMS and create template-driven web designs Use jQuery plugins to enhance the "feel" of your CMS A step-by-step explanatory tutorial to get your hands dirty in building your own CMS         Read more about this book       (For more resources on this subject, see here.) After completing this article, the CMS could be considered "complete", in that almost every other requested feature can be supplied by writing a plugin for it. However, it should be noted that a CMS never is actually complete, because each new website may bring a new request that is not yet catered for. Having said that, using plugins lets you at least complete a "core" engine and concentrate on providing hooks that allow further development to be done, outside that core. What are plugins A plugin is a module of code that can be dropped into a directory and enabled, to give a CMS extra capabilities. Plugins need to be able to change the output and do other tasks, so it is necessary to add various "hooks" throughout the code where the plugins can apply their code. A very important reason for adding a plugin architecture to a CMS is that it lets you stabilize the core code. The core is basically the code that will be available in every instance of the CMS, as opposed to plugin code, which may or may not be present in any particular instance of the CMS. With a core piece of code that is deemed "complete", it becomes easier to manage bugs. Because you are not always adding to the core code, you are not actively adding to the potential number of bugs. In a CMS which does not have a stable core, any change to the central code can affect just about anything else. You really need to get your CMS to a stage where you are no longer developing the central engine. Instead, you are working mostly on external plugins and maybe occasional bug fixes to the core, as they are found. In my case, for example, I worked for years on building up a CMS before getting around to building in plugins. Every change that was requested was built into the core code. Usually, only the fully-tested code at that time would be the new code, so very often we would miss a problem that the new code would have caused somewhere else in the CMS. Often, this problem would not show up for weeks, so it would not be obvious what the problem was related to! When all the development of a CMS is shifted to plugins, it becomes less likely that the core is at fault when a problem occurs. Because plugins, by their nature, tend to be isolated pieces of code, if a bug does appear, it is very likely the bug is within the plugin's code and not anywhere else. Also, because plugins allow a person to develop without touching the core engine, it is possible for the external teams or individuals to create their own plugins that they can use with the engine, without needing to understand all the parts of the core engine. One more advantage is that if the plugin architecture is solid, it is possible for development to continue on the core completely separately from the plugins, knowing that plugins from one version of the CMS will most likely work with a core from another version. Events in the CMS One example of a hook is event triggers. In JavaScript (and therefore jQuery), there is the concept of events, where you can set a block of code to run when a certain trigger happens. For example, when you move your mouse over an element, there are a number of potential trigger points—onmouseover, onmouseenter, onmousemove (and possibly others, depending on the context). Obviously, PHP does not have those events, as it's a server-side language. But it is possible to conceive of triggers for your CMS that you could potentially hook onto. For example, let's say you've just finished figuring out the page content. At this point, you may want to trigger a page-content-created event. This could (and will, in this article) be used by a Page Comments plugin to tack on the comments thread, and any required forms, to the end of that page content. Another example: Let's say you want to create a custom log for your own purposes. You would then be interested in a start trigger that can be used to initialize certain values, such as a timer. After the output has been sent, a finish trigger that can be used to tally up a number of figures (compilation time, memory used, size of rendered output, and so on) and record them in a file or database before the script finishes. Page types In some cases, you will want the page content to be totally converted. Instead of showing a page body as normal, you may want to show an image gallery or a store checkout. In this case, you would need to create a "page type" block of code, which the frontend will use instead of the usual page data render() call. In the admin area, this might also require using a customized form instead of the usual rich text editor. Admin sections The admin area may need to have new sections added by a plugin. In the Events section, we described a logging plugin. A perfect complement to that is a graphing log viewer, which would be shown as a completely new admin section and have its own entry in the admin menu. Page admin form additions You may also want to add extra forms to all the Page forms in the admin, regardless of what page type it is. For example, if you create a security plugin and want to protect various pages depending on who is viewing it, you will need to be able to choose which users or groups have access and what to display if the current user does not have full access. This requires an additional form in the Page admin. It is very difficult to describe all the possible plugin uses, and the number of triggers that may be required. The easiest way to proceed is to just adjust the engine as required. If it turns out you forgot to add an event trigger at some point, it should be a small matter to just add it in at that point without affecting the core code beyond that addition. Example plugin configuration Create a directory called /ww.plugins. Each plugin you create will be placed in a directory—one directory per plugin. For our first example, we're going to build a Page Comments plugin, which will allow visitors to your site to leave comments on your pages. On the admin side, we will need to provide methods to maintain the submitted comments per page and for the whole site. Anyway, create a directory to hold the plugin called /ww.plugins/page-comments. The CMS will expect the plugin configuration for each plugin to be in a file named plugin.php. So the configuration for the Page Comments plugin /ww.plugins/page-comments/plugin.php is as follows: <?php $plugin=array( 'name' => 'Page Comments', 'description' => 'Allow your visitors to comment on pages.', 'version' => '0', 'admin' => array( 'menu' => array( 'Communication>Page Comments' => 'comments' ) , 'page_tab' => array( 'name' => 'Comments', 'function' => 'page_comments_admin_page_tab' ) ), 'triggers' => array( 'page-content-created' => 'page_comments_show' ) ); The plugin.php files at least contain an array named $plugin, which describes the plugin. For now, let's look at what the current example says. All of these options, except the first two, are optional. First, we define a name, "Page Comments". This is only ever used in the admin area, when you are choosing your plugins. The same is true of the description field. The version field is used by the CMS to tell whether a plugin is up-to-date or if some automatic maintenance is needed. This will be explained in more detail later in this article. Next, we have the admin array, which holds details of the admin-only functions. The menu array is used to edit the admin menu, in case you need to add an admin section for the plugin. In this case, we will add an admin section for Page Comments, which will let you set site-wide settings and view comments site-wide. If a new tab is to be added to the page admin section, this tab is described in the page_tab array. name is what appears in the tab header, and function is the name of a PHP function that will be called to generate the tab content. Finally, the triggers array holds details of the various triggers that the plugin should react to. Each trigger calls a function. Obviously, this is not a complete list, and it is not possible to ever have a complete list, as each new circumstance you are requested to write for may bring up a need for a trigger or plugin config setting that you had not thought of. However, there are less and less additions, as the plugin architecture becomes more complete. From the plugin configuration, you can see that there are some functions named, which we have not defined. You should define those functions in the same file: function page_comments_admin_page_tab($PAGEDATA){ require_once SCRIPTBASE.'ww.plugins/page-comments/' .'admin/page-tab.php'; return $html; } function page_comments_show($PAGEDATA){ if(isset($PARENTDATA->vars->comments_disabled) && $PARENTDATA->vars->comments_disabled=='yes') return; require_once SCRIPTBASE.'ww.plugins/page-comments/' .'frontend/show.php'; } The functions are prefixed with an identifier to make sure that they don't clash with the functions from other plugins. In this case, because the plugin is named Page Comments, the prefix is page_comments_. The functions here are essentially stubs. Plugins will be loaded every time any request is made to the server. Because of this, and the obvious fact that not all the functions would be needed in every request, it makes sense to keep as little code in it as possible in the plugin.php files. In most cases, triggers will be called with just the $PAGEDATA object as a parameter. Obviously, in cases in the admin area where you're not editing any particular page this would not make sense, but for most plugins, to keep the function calls consistent, the only parameter is $PAGEDATA. Enabling plugins We have defined a plugin. We could make it such that when you place a plugin in the /ww.plugins directory, it is automatically enabled. However, if you are creating a CMS that you intend to reuse for a lot of other clients, it is a lot easier to simply copy the entire CMS source and reconfigure, than to copy the CMS source and then clear out the existing plugins and repopulate carefully with new ones that you would download from a repository that you keep somewhere else. So, what we do is we give the admin a maintenance page where they choose the plugins they want to load. The CMS then only loads those and does not even look at the other directories. Edit the /ww.admin/header.php file and add a new link (highlighted) to the plugin admin section: <a href="/ww.admin/themes.php">Themes</a>     Plugins     Log Out   We will be changing the admin menu later in this article to make it customizable more easily, but for now, add in that link manually. Now create the /ww.admin/plugins.php file: <?php require 'header.php'; echo ' Plugin Management '; echo ' '; echo 'Users'; echo 'Themes'; echo 'Plugins'; echo ' '; echo ' '; echo ' Plugin Management '; require 'plugins/list.php'; echo ' '; require 'footer.php'; You'll have noticed that this is similar to the /ww.admin/themes.php and /ww.admin/users.php files. They're all related to site-wide settings, so I've placed links to them all in the left-menu. Edit those files and add in the new Plugins link to their menus. Before we create the page for listing the enabled plugins, we must first set up the array of enabled plugins in /ww.incs/basics.php, by adding this to the end of the file: // { plugins $PLUGINS=array(); if (isset($DBVARS['plugins'])&&$DBVARS['plugins']) { $DBVARS['plugins']=explode(',',$DBVARS['plugins']); foreach($DBVARS['plugins'] as $pname){ if (strpos('/',$pname)!==false) continue; require SCRIPTBASE . 'ww.plugins/'.$pname.'/plugin.php'; $PLUGINS[$pname]=$plugin; } } else $DBVARS['plugins']=array(); // } As you can see, we are again referencing the $DBVARS array in the /.private/config.php. Because we already have a function for editing that, all we need to do to change the list of enabled or disabled plugins, and create and maintain the $DBVARS['plugins'] array, making sure to resave the config file after each change. What the code block does is that it reads in the plugin.php file for each enabled plugin, and saves the $plugin array from each file into a global $PLUGINS array. The $DBVARS['plugins'] variable is an array, but we'll store it as a comma-delimited string in the config file. Edit config_rewrite() in the same file and add this highlighted line: $tmparr=$DBVARS; $tmparr['plugins']=join(',',$DBVARS['plugins']); $tmparr2=array(); We'll enhance the plugin loader in a short while. In the meantime, let's finish the admin plugin maintenance page. Create the directory /ww.admin/plugins, and in it, add /ww.admin/plugins/list.php: <?php echo ' '; echo ' '; // { list enabled plugins first foreach($PLUGINS as $name=>$plugin){ echo '', '', '', ''; } // } // { then list disabled plugins $dir=new DirectoryIterator(SCRIPTBASE . 'ww.plugins'); foreach($dir as $plugin){ if($plugin->isDot())continue; $name=$plugin->getFilename(); if(isset($PLUGINS[$name]))continue; require_once(SCRIPTBASE.'ww.plugins/'.$name.'/plugin.php'); echo '', '', '', '', ''; } // } echo ' Plugin Name Description ',htmlspecialchars(@$plugin['name']),' ',htmlspecialchars(@$plugin['description']),' disable ',htmlspecialchars($plugin['name']),' ',htmlspecialchars($plugin['description']),' enable '; When viewed in a browser, it displays like this: The script displays a list of already-enabled plugins (we have none so far), and then reads the /ww.plugins directory for any other plugins and adds them along with an "enable" link. Now we need to write some code to do the actual selection/enabling of the plugins. While it would be great to write some jQuery to do it in an Ajaxy way (so you click on the enable link and the plugin is enabled in the background, without reloading the page), there are too many things that might cause problems. For instance, if the plugin caused new items to appear in the menu, we'd have to handle that. If the plugin changed the theme, or did anything else that caused a layout change, we'd have to handle that as well. So instead, we'll do it the old-fashioned PHP way—you click on enable or disable, which does the job on the server, and then reloads the plugin page so you can see the change. Create the /ww.admin/plugins/enable.php file: <?php require '../admin_libs.php'; if(!in_array($_REQUEST['n'],$DBVARS['plugins'])){ $DBVARS['plugins'][]=$_REQUEST['n']; config_rewrite(); } header('Location: /ww.admin/plugins.php'); It simply adds the requested plugin to the $DBVARS['plugins'] array, then rewrites the config and redirects the browser back to the plugins page. When clicked, the page apparently just reloads, and the plugin's link changes to disable. The opposite script is just as simple. Write this code block in the file /ww.admin/plugins/disable.php: <?php require '../admin_libs.php'; if(in_array($_REQUEST['n'],$DBVARS['plugins'])){ unset($DBVARS['plugins'][ array_search($_REQUEST['n'],$DBVARS['plugins']) ]); config_rewrite(); } header('Location: /ww.admin/plugins.php'); In this case, all we needed to do was to remove the plugin name from $DBVARS['plugins'] by unsetting its position in the array. Plugins are now very simply set up. Here's a screenshot of that page with a number of plugins enabled and disabled. I copied some plugins from a more mature copy of the CMS that I have. We will be looking at a few of them, and building one or two others: The enabled plugins are moved to the top of the list to make them more visible and the rest are shown below them.
Read more
  • 0
  • 0
  • 1116
article-image-building-admin-interface-drupal-7
Packt
03 Dec 2010
11 min read
Save for later

Building an admin interface in Drupal 7

Packt
03 Dec 2010
11 min read
The User Warn module In this article we will be creating the User Warn module. This module allows administrators to send users a warning via e-mail when that user violates a site's terms of service or otherwise behaves in a way that is inappropriate. The User Warn module will implement the following features: The module will expose configuration settings to site administrators, including default mail text This e-mail will include Drupal tokens, which allow the admin to replace and/or add site-specific variables to the e-mail Site administrators will be able to send a user mail via a new tab on their user profile page Warning e-mails will be sent using Drupal's default mail implementation Starting our module We will begin by creating a new folder for our module called user_warn in the sites/default/modules directory in our Drupal installation. We can then create a user_warn.info file as shown in the following: ;$Id$ name = User Warn description = Exposes an admin interface to send behavior warning e-mails to users. core = 7.x package = Drupal 7 Development files[] = user_warn.module You should be pretty familiar with this now. We will also create our user_warn.module file and add an implementation of hook_help() to let site administrators know what our module does. http://api.drupal.org/api/function/hook_menu/7 The example is as follows: /** * Implement hook_menu(). */ function user_warn_menu() { $items = array(); $items['admin/config/people/user_warn'] = array( 'title' => 'User Warn', 'description' => 'Configuration for the User Warn module.', 'page callback' => 'drupal_get_form', 'page arguments' => array('user_warn_form'), 'access arguments' => array('administer users'), 'type' => MENU_NORMAL_ITEM, ); $items['user/%/warn'] = array( 'title' => 'Warn', 'description' => 'Send e-mail to a user about improper site behavior.', 'page callback' => 'drupal_get_form', 'page arguments' => array('user_warn_confirm_form', 1), 'access arguments' => array('administer users'), 'type' => MENU_LOCAL_TASK, ); return $items; } Like many Drupal hook implementations, hook_menu() returns a structured associative array with information about the menu items being defined. The first item in our example defines the module configuration page, and the second one defines the user tab where administrators can go to send the actual e-mail. Let's look at the first item in more detail. Menu items are keyed off their path. This is an internal Drupal path with no leading or trailing slashes. This path not only defines the location of a page, but also its place in the menu hierarchy, with each part of the URL being a child of the last. In this example, people is a child of config which is itself a child of admin. If a requested path does not exist, Drupal will work its way up the hierarchy until it encounters a page that does exist. You can see this in action by requesting admin/config/people/xyzzy which displays the page at admin/config/people. If you are creating a menu item for site administration it must begin with admin. This places it into Drupal's administrative interface and applies the admin theme defined by the site settings. Module-specific settings should always be present under admin/config. Drupal 7 offers several categories which module developers should use to better organize their settings according to Drupal functional groups like People and Permissions or Content Authoring. The value associated with this key is itself an associative array with several keys that define what action should be taken when this URL is requested. We can now look at those in detail. The first item defines your page title: 'title' => 'User Warn', This is used in a variety of display contexts—as your page's heading, in the HTML &lttitle&gt tag and as a subheading in the administration interface in combination with the description (if you are defining an administrative menu item). 'description' => 'Configuration for the User Warn module.', The description is just that—a longer text description of the page that this menu item defines. This should provide the user with more detailed information about the actions they can take on this page. This description is also used as the title tag when you hover over a link. Menu item titles and descriptions are passed through t() internally by Drupal, so this is one case where we don't need to worry about doing that ourselves. For an administration page, these two items define how your page is listed in Drupal's admin area as shown in the following: The next two items define what will happen when your page is requested: 'page callback' => 'drupal_get_form', 'page arguments' => array('user_warn_form'), 'page callback' defines the function that will get called (without the parentheses) and 'page arguments' contains an array of arguments that get passed to this function. Often you will create a custom function that processes, formats, and returns specific data. However, in our case we are calling the internal Drupal function drupal_get_form() that returns an array as defined by Drupal's Form API. As an argument we are passing the form ID of the form we want to display. The fifth item controls who can access your page. 'access arguments' => array('administer users'), 'access arguments' takes an array containing a permissions strings. Any user who has been assigned one of these permissions will be able to access this page. Anyone else will be given an Access denied page. Permissions are defined by modules using hook_permission(). You can see a full list of the currently defined permissions at admin/people/permissions as shown: You can see the 'administer users' permission at the bottom of this list. In the preceding example, only the Administrator role has this permission, and as a result only those users assigned this role will be able to access our page. Note that the titles of the permissions here do not necessarily match what you will need to enter in the access arguments array. Unfortunately, the only good way to find this information is by checking the hook_perm() implementation of the module in question. The final item defines what type of menu item we are creating: 'type' => MENU_NORMAL_ITEM, The 'type' is a bitmask of flags that describe what features we want our menu item to have (for instance, whether it is visible in the breadcrumb trail). Drupal defines over 20 constants for menu items that should cover any situation developers will find themselves in. The default type is MENU_NORMAL_ITEM, which indicates that this item will be visible in the menu tree as well as the breadcrumb trail. This is all the information that is needed to register our path. Now when Drupal receives a request for this URL, it will return the results of drupal_get_form(user_warn_form). Drupal caches the entire menu, so new/updated menu items will not be reflected immediately. To manually clear the cache, visit Admin | Configuration | Development | Performance and click on Clear all caches. Using wildcards in menu paths We have created a simple menu item, but sometimes simple won't do the job. In the User Warn module we want to have a menu item that is tied to each individual user's profile page. Profile pages in Drupal live at the path user/&ltuser_id&gt, so how do we create a distinct menu item for each user? Fortunately the menu system allows us to use wildcards when we define our menu paths. If you look at the second menu item defined in the preceding example, you will see that its definition differs a bit from our first example. $items['user/%/warn'] = array( 'title' => 'Warn', 'description' => 'Send e-mail to a user about improper site behavior.', 'page callback' => 'drupal_get_form', 'page arguments' => array('user_warn_confirm_form', 1), 'access arguments' => array('administer users'), 'type' => MENU_LOCAL_TASK, ); The first difference is that the path is defined with % as one of the path entries. This indicates a wildcard; anything can be entered here and the menu item's hierarchy will be maintained. In Drupal, that will always be a user's ID. However, there is nothing stopping any user from entering a URL like user/xyzzy/warn or something else potentially more malicious. Your code should always be written in such a way as to handle these eventualities, for instance by verifying that the argument actually maps to a Drupal user. This would be a good improvement. The other difference in this example is that we have added 1 as an additional argument to be passed to our page callback. Each argument in a menu item's path can be accessed as an argument that is available to be passed to our page callback, starting with 0 for the root argument. So here the string user is item 0, and the user's ID is item 1. To use the user's ID as a page callback argument, we reference it by its number. The result in this case is that the user's ID will be passed as an additional argument to drupal_get_form(). We have one other difference in this second menu item: 'type' => MENU_LOCAL_TASK, We have defined our type as MENU_LOCAL_TASK. This tells Drupal that our menu item describes actions that can be performed on the parent item. In this example, Warn is an action that can be performed on a user. These are usually rendered as an additional tab on the page in question, as you can see in the following example user profile screen: Having defined the paths for our pages through hook_menu(), we now need to build our forms. Form API In standard web development, one of the most tedious and unrewarding tasks is defining HTML forms and handling their submissions. Lay out the form, create labels, write the submission function, figure out error handling, and the worst part is that from site to site much of this code is boilerplate—it's fundamentally the same, differing only in presentation. Drupal's Form API is a powerful tool allowing developers to create forms and handle form submissions quickly and easily. This is done by defining arrays of form elements and creating validation and submit callbacks for the form. In past versions of Drupal, Form API was commonly referred to as FAPI. However, Drupal 7 now has three APIs which could fit this acronym—Form API, Field API and File API. We will avoid using the acronym FAPI completely, to prevent confusion, but you will still encounter it widely in online references. Form API is also a crucial element in Drupal's security. It provides unique form tokens that are verified on form submission, preventing Cross-site Request Forgery attacks, and automatically validating required fields, field lengths, and a variety of other form element properties. Using drupal_get_form() In our first menu implementation seen earlier, we defined the page callback as drupal_get_form(). This Form API function returns a structured array that represents an HTML form. This gets rendered by Drupal and presented as an HTML form for the user. drupal_get_form() takes a form ID as a parameter. This form ID can be whatever you want, but it must be unique within Drupal. Typically it will be &ltmodule_name&gt_&ltdescription&gt_form. The form ID is also the name of the callback function drupal_get_form() will call to build your form. The specified function should return a properly formatted array containing all the elements your form needs. Since the form ID also serves as the form's callback function, it must be a valid PHP variable name. Spaces and hyphens are not allowed. All form IDs should be prefaced by the name of your module followed by an underscore, in order to prevent name collision. Other parameters can be passed into drupal_get_form() in addition to the form ID. These extra parameters simply get passed through to the callback function for its own use. In Drupal 6, drupal_get_form() returned a fully rendered HTML form. This has been changed in Drupal 7 in order to allow more flexibility in theming and easier form manipulation. drupal_get_form() now returns an unrendered form array which must be passed to drupal_render() for final output. In the preceding example the menu system handles the change transparently, but other code converted from Drupal 6 may need to be changed.
Read more
  • 0
  • 0
  • 3487

article-image-creating-custom-themes-and-zen-drupal-6
Packt
01 Dec 2010
9 min read
Save for later

Creating Custom Themes and Zen in Drupal 6

Packt
01 Dec 2010
9 min read
Drupal 6 Theming Cookbook Over 100 clear step-by-step recipes to create powerful, great-looking Drupal themes Take control of the look and feel of your Drupal website Tips and tricks to get the most out of Drupal's theming system Learn how to customize existing themes and create unique themes from scratch Part of Packt's Cookbook series: Each recipe is a carefully organized sequence of instructions to complete the task as efficiently as possible         Read more about this book       (For more resources on Drupal, see here.) Introduction Sub-themes of core and contributed themes are convenient and efficient in modifying and reusing elements of their base themes, circumstances often require a completely unique approach specific to our site. Custom themes are the solution for websites which demand a fresh look, using complex layouts, or need so much customization that it would be prudent to start with a clean slate. Custom themes are the equivalent of handcrafted pieces of art as the themer controls every piece of the puzzle from a design or implementational point of view. This includes setting up the theme using .info files, choosing the layout, implementing it in a page template, adding regions, styling nodes using node templates, blocks using block templates, and so on. But over time, developers have identified a list of common tasks, characteristic layouts, and file and folder hierarchies which are logical, efficient, and promote reuse. This has evolved into what have been dubbed starter themes, themes upon which custom themes are built, usually as sub-themes. The most popular starter theme for Drupal is Zen. As advertised on its project page, the Zen theme is a flexible standards-compliant and semantically correct XHTML theme that can be highly modified through CSS and an enhanced version of Drupal's template system. It is designed in modular fashion making it straightforward to change layouts, override templates and theme functions, and to add or remove features. Additionally, the Zen theme comes with extensive documentation within each file which make things all the more convenient. With respect to CSS, Zen maintains a number of well documented CSS files segregated by functionality or location. For example, layout rules are contained within a dedicated layout.css (or similar) file and page backgrounds are styled within page-backgrounds.css and so on. This makes it convenient when it comes to managing and tracking code changes. A Zen-based theme contains the following file and folder structure: File/folder name Purpose template.php A file where theme overrides and other theme and engine-related code is placed. theme-settings.php A file where settings particular to a theme can be placed. These settings are usually exposed on the theme's configuration page. css/ A folder to store stylesheets. images/ A folder to store images used in the theme. images-source/ The folder where the source files for the optimized images in the images folder are available. js/ A folder to store JavaScript files. templates/ A folder where tpl.php template files are to be placed. There are a number of other starter themes available on drupal.org. Some of the more popular ones include Fusion (http://drupal.org/project/fusion), Blueprint (http://drupal.org/project/blueprint), Ninesixty (http://drupal.org/ project/ninesixty), and Adaptivetheme (http://drupal.org/project/ adaptivetheme). We will be looking only at the Zen starter theme in this article. Clearing the theme registry Before we begin, we need to familiarize ourselves with a seemingly trivial yet crucial task that needs to be performed on a routine basis during theme development—clearing the theme registry. The theme registry is essentially a table that Drupal uses to list and track the files and features of a theme, as well as the theme functions which are being exposed by modules and the theme itself. While it is recommended practice to turn on Drupal's cache feature only for production sites, the theme registry is built and cached regardless of other caching options. As a result, any changes that affect the structure of the theme will necessitate the clearing of the theme registry. Getting ready Rebuilding the registry is an intensive operation which is required only when changes have been made to the theme's files. How to do it... There are a number of ways of clearing the registry. In a stock Drupal installation, visiting admin/settings/performance (Home | Administer | Site configuration | Performance) and clicking on the Clear cached data button will clear all cached data, including the registry, and force a rebuild. A shortcut It is sometimes handy to know that the cache and registry can also be cleared by visiting admin/build/themes (Home | Administer | Site building | Themes) and just clicking the Save configuration button. However, during development or debugging, we will want to clear the registry with great regularity. Rather than having to do so manually, it is often handy to be able to instruct Drupal to perform this operation automatically on every page load. Some themes, including the Zen-based theme which we will be familiarizing ourselves with later in this article, offer an option on their configuration pages to rebuild the registry on every page load. While this is certainly convenient, the recommended method of managing this and other development-oriented operations is through the use of the Devel module. As the name suggests, the Devel module is one which is tailor-made for use during development. It can be downloaded from http://drupal.org/project/devel. Once the module has been downloaded and installed, navigate to admin/settings/devel (Home | Administer | Site configuration | Devel settings) where the option to Rebuild the theme registry on every page load can be enabled. How it works... Drupal maintains a cache of all .info files, template files, and theme functions in the theme registry. This registry is a part of the cache table in the Drupal database. When we click on the Clear cache data button in the performance settings page, all Drupal is doing is clearing this entry in the cache table, which automatically forces a rebuild of the registry. The Devel module does the same thing when the Rebuild the theme registry on every page load setting is enabled, except that it does this automatically on every page view. It is important to keep in mind that rebuilding the registry, or for that matter, clearing any of the caches is an expensive operation which adversely affects the performance of the site. Therefore, it is recommended that this setting only be enabled during development and not in production sites. Clearing the registry is an important factor to keep in mind during development and especially during debugging. There's more... The Devel module also provides a block with handy shortcuts to oft-used areas of the site. Clearing the cache using the Development block The Devel module provides a Development block which can be enabled via the block management page at admin/build/blocks (Home | Administer | Site building | Blocks). Once enabled, the block lists, as in the following screenshot, a number of links to perform operations such as emptying the Drupal cache, rebuilding the menu cache, and even reinstalling modules. Emptying the cache will also force a rebuild of the theme registry. Creating a theme from scratch While we have previously looked at installing contributed themes and extending base themes using sub-themes, this recipe will outline the steps required to create a custom theme. Getting ready It is assumed that the sites/all/themes folder has already been created. This is the recommended location to place custom and contributed themes. How to do it... While creating a brand new custom theme, files such as page.tpl.php will need to be explicitly defined and there is no base theme. Create a folder with the new theme's name inside the sites/all/themes folder. In this example, we are going to call our theme mytheme. Create a file named mytheme.info and open it in an editor. Add details about the theme as follows: name = My themedescription = My custom themecore = 6.xengine = phptemplate Save the file. Visit the theme administration page at admin/build/themes (Home | Administer | Site building | Themes) and we should be able to see a new entry for our theme. Enable the theme by checking its Enabled checkbox. Also, set it as the default theme by selecting its Default radio button. Click the Save configuration button at the bottom of the page to save the changes. How it works... Just as with other themes, Drupal scans the sites/all/themes folder looking for .info files which indicate the presence of a theme. Seeing mytheme.info, it parses the file and loads the details of the theme, and saves them in the database. When the new theme is enabled, what we will see is largely unstyled content not unlike the following screenshot. The problem here is that we have not specified any CSS stylesheets to ay out the page. The only styles being loaded are those that are module-specific as opposed o theme-specific. The styles being used in the preceding screenshot are as follows: <link type="text/css" rel="stylesheet" media="all"href="/mysite/modules/node/node.css?u" /><link type="text/css" rel="stylesheet" media="all"href="/mysite/modules/system/defaults.css?u" /><link type="text/css" rel="stylesheet" media="all"href="/mysite/modules/system/system.css?u" /><link type="text/css" rel="stylesheet" media="all"href="/mysite/modules/system/system-menus.css?u" /><link type="text/css" rel="stylesheet" media="all"href="/mysite/modules/user/user.css?u" /> As we can see, the only stylesheets in evidence are those belonging to core modules and none from our theme. In addition, Drupal has noticed that we do not have any template files in our theme, most notably, page.tpl.php. Therefore, it has loaded an inbuilt page template file from modules/system/page.tpl.php and used it instead. Similarly, it is using the node.tpl.php file from modules/node/ as the basis for each node's layout. In other words, we have a lot of work ahead of us in getting things up and running especially if our eventual requirements are going to be complicated. As we will see in the next recipe, this is one of the reasons why most themers prefer to use a starter theme and hit the ground running.
Read more
  • 0
  • 0
  • 1196

article-image-advanced-theme-liferay-user-interface-development
Packt
01 Dec 2010
16 min read
Save for later

Advanced Theme in Liferay User Interface Development

Packt
01 Dec 2010
16 min read
Liferay User Interface Development Develop a powerful and rich user interface with Liferay Portal 6.0 Design usable and great-looking user interfaces for Liferay portals Get familiar with major theme development tools to help you create a striking new look for your Liferay portal Learn the techniques and tools to help you improve the look and feel of any Liferay portal A practical guide with lots of sample code included from real Liferay Portal Projects free for use for developing your own projects Changing theme.parent property in theme Liferay provides four different theme implementations out-of-box in the ${PORTAL_ROOT_HOME}/html/themes/ folder as shown in the following screenshot: When you create a new theme in the themes/ directory of the Plugins SDK, the Ant script will copy some files to the new theme from one of these three folders, _styled/, _unstyled/, and classic/, which can be specified in the ${PLUGINS_SDK_HOME}/themes/build-common-theme.xml file. For example, you can go to the ${PLUGINS_SDK_HOME}/themes/ directory and run create.bat palmtree "Palm-Tree Publications theme" on Windows or ./create.sh palmtree "Palm-Tree Publications theme" on Linux, respectively. Or you can use Liferay IDE to create same New Liferay Theme Plugin Project. This will create a theme folder named palmtree-theme in the Liferay Plugins SDK environment. When you run ant to build and deploy the theme and then apply the newly created theme to your page, you will find out that the look and feel of the page is messed up, as shown in the following screenshot: If you are familiar with theme development in Liferay Portal 5.2, you may wonder what has happened to the theme created in Liferay Portal 6 Plugins SDK because you would expect a fully working theme with two simple commands, create and ant. This is because the following build.xml file in your theme specifies _styled as the value for the theme.parent property: <?xml version="1.0"?> <project name="palmtree-theme" basedir="." default="deploy"> <import file="../build-common-theme.xml" /> <property name="theme.parent" value="_styled" /> </project> This means that when your newly created theme is built, it will copy all the files from the _styled/ folder in the ${PORTAL_ROOT_HOME}/html/themes/ directory to the docroot/ folder of your theme. The default _styled/ folder does not have enough files to create a completely working theme and that is why you see a messed-up page when the theme is applied to a page. The reason why this default _styled/ folder does not include enough files is that some Liferay users prefer to have minimal set of files to start with. You can modify the build.xml file for your theme in the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/ folder by changing value of the theme.parent property from _styled to classic, if you prefer to use the Classic theme as the basis for your theme modification. <property name="theme.parent" value="classic" /> Now you will see that your page looks exactly the same as that with the Classic theme after you build and apply your theme to the page (refer to the following screenshot): Adding color schemes to a theme When you create a theme in the Plugins SDK environment, the newly created theme by default supports one implementation and does not automatically have color schemes as variations of the theme. This fits well if you would like to have a consistent look and feel, especially in terms of the color display, across all the pages that the theme is applied to. In your portal application, you might have different sites with slightly different look and feel for different groups of users such as physicians and patients. You might also need to display different pages such as public pages with one set of colors and the private pages with a different set of colors. However, you don't want to create several different themes for reasons such as easy maintenance. In this case, you might consider creating different color schemes in your theme. Color schemes are specified using a Cascading Style Sheets (CSS) class name, with which you are able to not only change the colors, but also choose different background images, border colors, and so on. In the previous section, we created a PalmTree Publication theme, which takes the Classic theme as its parent theme. Now we can follow the mentioned steps to add color schemes to this theme: Copy the ${PORTAL_ROOT_HOME}/webapps/palmtree-theme/WEB-INF/liferay-look-and-feel.xml file to the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/WEB-INF/ folder. Open the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/WEB-INF/liferay-look-and-feel.xml file in your text editor. Change <theme id="palmtree" name="PalmTree Publication Theme" /> as shown in the highlighted lines here, and save the change: <look-and-feel> <compatibility> <version>6.0.5+</version> </compatibility> <theme id="palmtree" name="Palm Tree Publications Theme"> <color-scheme id="01" name="Blue"> <css-class>blue</css-class> <color-scheme-images-path>${images-path}/color_schemes/ blue</color-scheme-images-path> </color-scheme> <color-scheme id="02" name="Green"> <css-class>green</css-class> </color-scheme> <color-scheme id="03" name="Orange"> <css-class>orange</css-class> </color-scheme> </theme> </look-and-feel> Go to the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/palmtree-theme/_diffs/ folder and create a css/ subfolder. Copy both custom.css and custom_common.css from the ${PORTAL_ROOT_HOME}/html/themes/classic/_diffs/css/ folder to the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/css/ folder. This is to let the default styling handle the first color scheme blue. Create a color_schemes/ folder under the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/palmtree-theme/_diffs/css/ folder. Place one .css file for each of your additional color schemes. In this case, we are going to create two additional color schemes: green and orange. To make the explanation simpler, copy both the green.css and orange.css files from the ${PORTAL_ROOT_HOME}/html/themes/classic/_diffs/css/color_schemes/ folder to the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/css/color_schemes/ folder. Copy all images from the ${PORTAL_ROOT_HOME}/html/themes/classic/_diffs/images/ folder to the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/images/ folder. Add the following lines in your ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/css/custom.css file: @import url(color_schemes/green.css); @import url(color_schemes/orange.css); If you open either the green.css or the orange.css file, you will be able to identify the styling for the CSS by using a color prefix for each CSS definition. For example, in the orange.css file you would find that each item is defined like this: orange .dockbar { background-color: #AFA798; background-image: url(../../images/color_schemes/orange/dockbar /dockbar_bg.png); } .orange .dockbar .menu-button-active { background-color: #DBAC5C; background-image: url(../../images/color_schemes/orange/dockbar /button_active_bg.png); } In the green.css file, the style is defined like this: green .dockbar { background-color: #A2AF98; background-image: url(../../images/color_schemes/green/dockbar/ dockbar_bg.png); } .green .dockbar .menu-button-active { background-color: #95DB5C; background-image: url(../../images/color_schemes/green/dockbar/ button_active_bg.png); } Also, notice that the related images used for the different color schemes are included in the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/images/color_schemes/ folder. Re-build and deploy the PalmTree Publication theme. Log in as administrator and go to the Control Panel to apply this theme to the PalmTree Publications Inc. organization. You will be able to see three color schemes available under this theme. Select any of them to apply it to the Public Pages. Take a screenshot of the page when each of the color schemes is applied and save it as thumbnail.png in the corresponding blue, green, and orange subfolders in the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/images/color_scheme/ folder. Three screenshots are used to distinguish between the three color schemes in the Control Panel as seen in the following screenshot: The following screenshots shows how each of the three color schemes looks when applied to a portal page: As shown in above screenshot, color scheme blue has been applied on the Home page, by default. The following screenshot shows applying color scheme green on the current page. Of course, you would be able to apply color schemes blue or green in the entire site, if required. Similar to color schemes blue and green, you can apply color scheme orange as well on the current page or the entire site, as shown in following screenshot: So it works! The page background is with a hue of gray color. Now, what if we want to change the page background color to red for the green color schema? Update the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/css/color_schemes/green.css file as follows. The commented outline is the original content. The next line after the comment is the new content. The #FF0000 code is for the red color. body.green, .green .portlet { /*background-color: #F1F3EF;*/ background-color: #FF0000; } Re-deploy the PalmTree theme and refresh the portal page that uses the green color scheme. Now, you should be able to see the portal page with a red background color. As you can see, you can use theme color schemes to create some variants of the same theme without creating multiple themes. This is useful when you have different but related user roles such as physicians, nurses, and patients and you need to build a different site for each of them. You can use the color schemes to display each of these sites in a slightly different look and feel. Configurable theme settings There are many use cases where you would like to change some default settings in the theme so that you can modify the values after the theme is built and deployed. Fortunately, each theme can define a set of settings to make this configurable. The settings are defined as key-value pairs in the liferay-look-and-feel.xml file of the theme with the following syntax: <settings> <setting key="my-setting1" value="my-value1" /> <setting key="my-setting2" value="my-value2" /> </settings> These settings can then be accessed in the theme templates using the following code: $theme.getSetting("my-setting1") $theme.getSetting("my-setting2") For example, I need to create two themes—PalmTree Publications theme and AppleTree Publications theme. They are exactly the same except for some differences in the footer content that includes copyright, terms of use, privacy policy, and so on. Instead of creating two themes packaged as separate .war files, we create one set of two themes that share the majority of the code including CSS, JavaScript, images, and most templates; but with one configurable setting and two different implementations of the footer Velocity files. Here is how this can be done: Open ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/WEB-INF/liferay-look-and-feel.xml in the above sample PalmTree theme. Copy the PalmTree theme section and paste it in the same file but after this section. Rename the values of id and name from palmtree and PalmTree Publications theme to appletree and AppleTree Publications theme in the second section. Add the following setting to the palmtree section before the color-scheme definition: <settings> <setting key="theme-id" value="palmtree" /> </settings> Add the following setting to the appletree section before the color-scheme definition: <settings> <setting key="theme-id" value="appletree" /> </settings> Find the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/WEB-INF/liferay-look-and-feel.xml file as follows: <look-and-feel> // ignore details <theme id="palmtree" name="PalmTree Publications Theme"> <settings> <setting key="theme-id" value="palmtree" /> </settings> <color-scheme id="01" name="Blue"> // ignore details </color-scheme> </theme> <theme id="appletree" name="AppleTree Publications Theme"> <settings> <setting key="theme-id" value="appletree" /> </settings> <color-scheme id="01" name="Blue"> // ignore details </color-scheme> </theme> </look-and-feel> Copy the portal_normal.vm file of the Classic theme from the ${PORTAL_ROOT_HOME}/html/themes/classic/_diffs/templates/ folder to the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/templates/ folder. Open the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/templates/portal_normal.vm file Replace the default footer section with the following code: #set ($theme_id = $theme.getSetting("theme-id")) #if ($theme_id == "palmtree") #parse ("$full_templates_path/footer_palmtree.vm") #else #parse ("$full_templates_path/footer_appletree.vm") #end Create your own version of the two footer Velocity templates in the ${PLUGINS_SDK_HOME}/themes/palmtree-theme/docroot/_diffs/templates/ folder. Add related CSS definitions for your footer in your custom.css file. Build and deploy the theme .war file. Now you should be able to see both the PalmTree and AppleTree themes when you go to the Control Panel to apply either theme to your page. Based on the theme to be used, you should also notice that your footer is different. Of course, we can take other approaches to implement a different footer in the theme. For example, you can dynamically get the organization or community name and render the footer differently. However, the approach we explained previously can be expanded to control the UI of the other theme components such as the header, navigation, and portlets. Portal predefined settings in theme In the previous section, we discussed that theme engineers can add configurable custom settings in the liferay-look-and-feel.xml file of a theme. Liferay portal can also include some predefined out-of-the-box settings such as portlet-setup-show-borders-default and bullet-style-options in a theme to control certain default behavior of the theme. Let us use portlet-setup-show-borders-default as an example to explain how Liferay portal controls the display of the portlet border at different levels. If this predefined setting is set to false in your theme, Liferay portal will turn off the borders for all portlets on all pages where this theme is applied to. <settings> <setting key="portlet-setup-show-borders-default" value="false" /> </settings> By default, the value is set to true, which means that all portlets will display the portlet border by default. If the predefined portlet-setup-show-borders-default setting is set to true, it can be overwritten for individual portlets using the liferay-portlet.xml file of a portlet as follows: <liferay-portlet-app> // ignore details <portlet> <portlet-name>sample</portlet-name> <icon>/icon.png</icon> <use-default-template>false</use-default-template> <instanceable>true</instanceable> <header-portlet-css>/css/main.css</header-portlet-css> <footer-portlet-javascript>/js/main.js</footer-portlet-javascript> <css-class-wrapper>sample-portlet</css-class-wrapper> </portlet> // ignore details </liferay-portlet-app> Set the use-default-template value to true if the portlet uses the default template to decorate and wrap content. Setting it to false will allow the developer to maintain the entire output content of the portlet. The default value is true. The most common use of this is when you want the portlet to look different from the other portlets or if you want the portlet not to have borders around the output content. The use-default-template setting of each portlet, after being set to either true or false, in the liferay-portlet.xml file, can be further overwritten by the portlet's popup CSS setting. Users with the appropriate permissions can change it by going to the Look and Feel | Portlet Configuration | Show Borders checkbox of the portlet, as shown in the next screenshot: Embedding non-instanceable portlets in theme One common requirement in theme development is to add some portlets in different components of a theme. For example, you might want to add the Sign In portlet in the header of your theme, the Web Content Search portlet in the navigation area, and the Site Map portlet in the footer area. All the Liferay out-of-the-box portlets can be referred in the ${PORTAL_ROOT_HOME}/WEB-INF/liferay-portlet.xml file. How can this be achieved? Well, it can be quite easy or pretty tricky to embed an out-of-the-box portlet in a theme. Embedding Dockbar and Breadcrumb portlets in a theme The Dockbar portlet is embedded at the very top in the default Classic theme in the portal_normal.vm file as shown next: #if($is_signed_in) #dockbar() #end In the same way, the Breadcrumb portlet can be embedded in the content area of the Classic theme in the portal_normal.vm file: #breadcrumbs() Embedding Language and Web Content Search portlets in a theme Some other Liferay out-of-the-box portlets such as the Language and Web Content Search portlets can be embedded in a theme or a layout template easily. For example, the Web Content Search portlet can be added to the far right side of the horizontal navigation area of your theme as follows in the navigation.vm file of your theme. <div id="navbar"> <div id="navigation" class="sort-pages modify-pages"> <div id="custom"> $theme.journalContentSearch() </div> // ignore details </div> </div> In the same way, the Language portlet can be embedded in the portal_normal.vm Velocity template file of your theme: $theme.language() Again, you need to add the necessary CSS definitions in your custom.css file to control the look and feel and the location of your embedded portlet(s). Embedding Sign In portlet in the header area of a theme Sometimes the theme design requires that the Liferay Sign In portlet be in the header area. By default, the Sign In portlet has a portlet border but we need to disable it. The previously mentioned approaches for embedding a portlet in a theme through Velocity attributes in a template do not work in this case because we need to customize the default UI of the embedded portlet. Instead, we can add the following code to the header section in the portal_normal.vm file in our sample PalmTree theme: #if(!$is_signed_in) #set ($locPortletId = "58") $velocityPortletPreferences.setValue("portlet-setup-show-borders", "false") #set($locRenderedPortletContent = $theme.runtime($locPortletId, "", $velocityPortletPreferences.toString())) $locRenderedPortletContent $velocityPortletPreferences.reset() #end After the theme is re-built and re-deployed, we can see that the Sign In portlet is rendered in the header area underneath the logo without the portlet border. The next step for us is to modify the custom.css file and the related files by adding CSS definition to control the location and look and feel of this Sign In portlet. The following screenshot shows the Sign In portlet in the header area and the Web Content Search portlet in the navigation area in a working theme in the production environment:
Read more
  • 0
  • 0
  • 5984
article-image-vaadin-portlets-liferay-user-interface-development
Packt
01 Dec 2010
1 min read
Save for later

Vaadin Portlets in Liferay User Interface Development

Packt
01 Dec 2010
1 min read
Vaadin portlets are developed with Vaadin framework. The Vaadin framework can also be used to develop standalone web applications. Liferay portal supports the Vaadin portlets. In this section, we will write a Vaadin portlet for Liferay portal using the Vaadin Eclipse plugin. Required software Install the following software for the development environment, if they are not already there: Eclipse Java EE IDE Liferay portal 6.x.x with Tomcat 6.0.x Configuring Tomcat 6.0 in Eclipse If you have not already done so, configure Tomcat 6.0 in Eclipse as follows: Start Eclipse. Click on Window | Preferences. Expand Server. Click on Runtime Environment. Click on Add .... Select Apache Tomcat v6.0. Click on Next. Click on Browse and open the tomcat-6.0.x directory. Click on Finish. Installing Vaadin Eclipse plugin You can automatically create a Vaadin portlet prototype for Liferay portal with the Vaadin Eclipse plugin. Here is how you can install it: Assuming that Eclipse is open. Click on Help. Select Install New Software ... Click on Add .... Input Name: Vaadin, Location: http://vaadin.com/eclipse. Click on OK. Click on Finish. The Vaadin Eclipse plugin will be installed. It will take several minutes.
Read more
  • 0
  • 0
  • 1572

article-image-user-interface-production
Packt
01 Dec 2010
10 min read
Save for later

User Interface in Production

Packt
01 Dec 2010
10 min read
  Liferay User Interface Development Develop a powerful and rich user interface with Liferay Portal 6.0 Design usable and great-looking user interfaces for Liferay portals Get familiar with major theme development tools to help you create a striking new look for your Liferay portal Learn the techniques and tools to help you improve the look and feel of any Liferay portal A practical guide with lots of sample code included from real Liferay Portal Projects free for use for developing your own projects         Read more about this book       (For more resources on this subject, see here.) This section will introduce how to add workflow capabilities to any custom assets in plugins. Knowledge Base articles will be used as an example of custom assets. In brief, the Knowledge Base plugin enables companies to consolidate and manage the most accurate information in one place. For example, a user manual is always updated; users are able to rate, to add workflow and to provide feedback on these Knowledge Base articles. Preparing a plugin—Knowledge Base First of all, let's prepare a plugin with workflow capabilities, called Knowledge Base. Note that the plugin Knowledge Base here is used as an example only. You can have your own plugin as well. What's Knowledge Base? The plugin Knowledge Base allows authoring articles and organize them in a hierarchy of navigable categories. It leverages Web Content articles, structures, and templates; allows rating on articles; allows commenting on articles; allows adding hierarchy of categories; allows adding tags to articles; exports articles to PDF and other formats; supports workflow; allows adding custom attributes (called custom fields); supports indexing and advanced search; allows using the rule engine and so on. Most importantly the plugin Knowledge Base supports import of a semantic markup language for technical documentation called DocBook. DocBook enables its users to create document content in a presentation-neutral form that captures the logical structure of the content; that content can then be published in a variety of formats, such as HTML, XHTML, EPUB, and PDF, without requiring users to make any changes to the source. Refer to http://www.docbook.org/. In general, the plugin Knowledge Base provides four portlets inside: Knowledge Base Admin (managing knowledge base articles and templates), Knowledge Base Aggregator (publishing knowledge base articles), Knowledge Base Display (displaying knowledge base articles) and Knowledge Base Search (the ability to search knowledge base articles). Structure The plugin Knowledge Base has following folder structure under the folder $PLUGIN_SDK_HOME/knowledge-base-portlet. admin: View JSP files for portlet Admin aggregator: View JSP files for portlet Aggregator display: View JSP files for portlet Display icons: Icon images files js: JavaScript files META-INF: Contains context.xml search: View JSP files for portlet Search WEB-INF: Web info specification; includes subfolders classes, client, lib, service, SQL, src, and tld As you can see, JSP files such as init.jsp and css_init.jsp are located in the folder $PLUGIN_SDK_HOME/knowledge-base-portlet. Services and models As you may have noticed, the plugin Knowledge Base has specified services and models with the package named com.liferay.knowledgebase. You would be able to find details at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/service.xml. Service-Builder in Plugins SDK will automatically generate services and models against service.xml, plus XML files such as portlet-hbm.xml, portlet-model-hints.xml, portlet-orm.xml, portlet-spring.xml, base-spring.xml, cluster-spring.xml,dynamic-data-source-spring.xml, hibernate-spring.xml, infrastructure-spring.xml, messaging-spring.xml, and shard-data-source-spring.xml under the folder $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/src/META-INF. The service.xml specified Knowledge Base articles as entries: Article, Comment, and Template. The entry Article included columns: article Id as primary key, resource Prim Key, group Id, company Id, user Id, user Name, create Date, modified Date, parent resource Prim Key, version, title, content, description, and priority; the entry Template included columns: template Id as primary key, group Id, company Id, user Id, user Name, create Date, modified Date, title, content, and description; while the entity Comment included columns: comment Id as primary key, group Id, company Id, user Id, user Name, create Date, modified Date, class Name Id, class PK, content and helpful. As you can see, the entity Comment could be applied on either any core assets or custom assets like Article and Template by using class Name Id and class PK. By the way, the custom SQL scripts were provided at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/src/custom-sql/default.xml. In addition, resource actions, that is, permission actions specification—are provided at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/src/resource-actions/default.xml. Of course, you can use Ant target build-wsdd to generate WSDD server configuration file $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/server-config.wsdd and to use Ant target build-client plus namespace-mapping.properties to generate web service client JAR file like $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/client/known-base-portlet-client.jar. In brief, based on your own custom models and services specified in service.xml, you can easily build service, WSDD, and web service client in plugins of Liferay 6 or above version. Adding workflow instance link First, you have to add a workflow instance link and its related columns and finder in $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/service.xml as follows. <column name="status" type="int" /> <column name="statusByUserId" type="long" /> <column name="statusByUserName" type="String" /> <column name="statusDate" type="Date" /> <!-- ignore details --> <finder name="R_S" return-type="Collection"> <finder-column name="resourcePrimKey" /> <finder-column name="status" /> </finder> <!-- ignore details --> <reference package-path="com.liferay.portal" entity="WorkflowInstance Link" /> As shown in the above code, the column element represents a column in the database, four columns like status, statusByUserId, statusByUserName, and statusDate are required for Knowledge Base workflow, storing workflow related status, and user info; the finder element represents a generated finder method, the method finder R_S is defined as Collection (an option) for return type with two columns, for example, resourcePrimkey and status; where the reference element allows you to inject services from another service.xml within the same class loader. For example, if you inject the Resource (that is, WorkflowInstanceLink) entity, then you'll be able to reference the Resource services from your service implementation via the methods getResourceLocalService and getResourceService. You'll also be able to reference the Resource services via the variables resourceLocalService and resourceService. Then, you need to run ANT target build-service to rebuild service based on newly added workflow instance link. Adding workflow handler Liferay 6 provides pluggable workflow implementations, where developers can register their own workflow handler implementation for any entity they build. It will appear automatically in the workflow admin portlet so users can associate workflow entities with available permissions. To make it happen, we need to add the Workflow Handler in $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/WEB-INF/liferay-portlet.xml of plugin as follows. <workflow-handler>com.liferay.knowledgebase.admin.workflow. ArticleWorkflowHandler</workflow-handler> As shown in the above code, the workflow-handler value must be a class that implements com.liferay.portal.kernel.workflow.BaseWorkflowHandler and is called when the workflow is run. Of course, you need to specify ArticleWorkflowHandler under the package com.liferay.knowledgebase.admin.workflow. The following is an example code snippet. public class ArticleWorkflowHandler extends BaseWorkflowHandler { public String getClassName(){/* get target class name */}; public String getType(Locale locale) {/* get target entity type, that is, Knowledge base article*/}; public Article updateStatus( int status, Map<String, Serializable> workflowContext) throws PortalException, SystemException {/* update workflow status*/}; protected String getIconPath(ThemeDisplay themeDisplay) {/* find icon path */ return ArticleLocalServiceUtil.updateStatus(userId, resourcePrimKey, status, serviceContext); }; } As you can see, ArticleWorkflowHandler extends BaseWorkflowHandler and overrode the methods getClassName, getType, updateStatus, and getIconPath. That's it. Updating workflow status As mentioned in the previous section, you added the method updateStatus in ArticleWorkflowHandler. Now you should provide implementation of the method updateStatus in com.liferay.knowledgebase.service.impl.ArticleLocalServiceImpl.java. The following is some example sample code: public Article updateStatus(long userId, long resourcePrimKey, int status, ServiceContext serviceContext) throws PortalException, SystemException { /* ignore details */ // Article Article article = getLatestArticle(resourcePrimKey, WorkflowConstants.STATUS_ANY); articlePersistence.update(article, false); if (status != WorkflowConstants.STATUS_APPROVED) { return article; } // Articles // Asset // Social // Indexer // Attachments // Subscriptions } As shown in above code, it first gets latest article by resourcePrimKey and WorkflowConstants.STATUS_ANY. Then, it updates the article based on the workflow status. And moreover, it updates articles display order, asset tags and categories, social activities, indexer, attachments, subscriptions, and so on. After adding new method at com.liferay.knowledgebase.service.impl.ArticleLocalServiceImpl.java, you need to run ANT target build-service to build services. Adding workflow-related UI tags Now it is time to add workflow related UI tags (AUI tags are used as an example) at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/admin/edit_article.jsp. First of all, add the AUI input workflow action with value WorkflowConstants.ACTION_SAVE_DRAFT as follows. <aui:input name="workflowAction" type="hidden" value=" <%= WorkflowConstants.ACTION_SAVE_DRAFT %>" /> As shown in above code, the default value of AUI input workflowAction was set as SAVE DRAFT with type hidden. That is, this AUI input is invisible to end users. Afterwards, it would be better to add workflow messages by UI tag liferay-ui:message, like a-new-version-will-be-created-automatically-if-this-content-is-modified for WorkflowConstants.STATUS_APPROVED, and there-is-a-publication-workflow-in-process for WorkflowConstants.STATUS_PENDING. <% int status = BeanParamUtil.getInteger(article, request, "status", WorkflowConstants.STATUS_DRAFT); %> <c:choose> <c:when test="<%= status == WorkflowConstants.STATUS_APPROVED %>"> <div class="portlet-msg-info"> <liferay-ui:message key="a-new-version-will-be-created- automatically-if-this-content-is-modified" /> </div> </c:when> <c:when test="<%= status == WorkflowConstants.STATUS_PENDING %>"> <div class="portlet-msg-info"> <liferay-ui:message key="there-is-a-publication-workflow-in-process" /> </div></c:when> </c:choose> And then add AUI workflow status tag aui:workflow-status at $PLUGIN_SDK_HOME/knowledge-base-portlet/docroot/admin/edit_article.jsp. <c:if test="<%= article != null %>"> <aui:workflow-status id="<%= String.valueOf(resourcePrimKey) %>" status="<%= status %>" version="<%= GetterUtil.getDouble(String. valueOf(version)) %>" /> </c:if> As you can see, aui:workflow-status is used to represent workflow status. Similarly you can find other AUI tags like a button, button_row, column, field_wrapper, fieldset, form, input, layout, legend, option, panel, script, and select. Finally you should add the JavaScript to implement the function publishArticle() as follows. function <portlet:namespace />publishArticle() { document.<portlet:namespace />fm.<portlet:namespace />workflowAction.value = "<%= WorkflowConstants.ACTION_PUBLISH %>"; <portlet:namespace />updateArticle(); } As you can see, the workflow action value is set as WorkflowConstants.ACTION_PUBLISH. You have added workflow capabilities on Knowledge Base articles in plugins. From now on, you will be able to apply workflow on Knowledge Base articles through Control Panel.
Read more
  • 0
  • 0
  • 1202