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

How-To Tutorials - Web Development

1802 Articles
article-image-authentication-zendauth-zend-framework-18
Packt
18 Nov 2009
6 min read
Save for later

Authentication with Zend_Auth in Zend Framework 1.8

Packt
18 Nov 2009
6 min read
Let's get started. Authentication versus Authorization Before we go any further, we need to first look at what exactly authentication and authorization is, as they are often misunderstood. Authorization is the process of allowing someone or something to actually do something. For example, if I go into a data centre, then the security guards control my authorization to the data centre and would, for instance, not allow me access to the server room if I was just a visitor but would if I worked there as a system admin. Authentication is the process of confirming someone or something's identity. For example, when I go to into the data centre the security guards will ask me for my identity, which most probably would be a card with my name and photo on. They use this to authenticate my identity. These concepts are very important so make sure you understand the difference. This is how I remember them: Authorization: Can they do this?Authentication: Are they who they say they are? Authentication with Zend_Auth To provide our authentication layer, we are going to use Zend_Auth. It provides an easy way to authenticate a request, obtain a result, and then store the identity of that authentication request. Zend_Auth Zend_Auth has three main areas—authentication adapters, authentication results, and identity persistence. Authentication adapters Authentication adapters work in a similar way to database adapters. We configure the adapter and then pass it to the Zend_Auth instance, which then uses it to authenticate the request. The following concrete adapters are provided by default: HTTP Digest authentication HTTP Basic authentication Database Table authentication LDAP authentication OpenID authentication InfoCard authentication All of these adapters implement the Zend_Auth_Adapter_Interface, meaning we can create our own adapters by implementing this interface. Authentication results All authentication adapters return a Zend_Auth_Result instance, which stores the result of the authentication request. The stored data includes whether the authentication request was successful, an identity if the request was successful, and any failure messages, if unsuccessful. Identity persistence The default persistence used is the PHP session. It uses Zend_Session_Namespace to store the identity information in the Zend_Auth namespace. There is one other type of storage available named NonPersistent, which is used for HTTP authentication. We can also create our own storage by implementing the Zend_Auth_Storage_Interface. Authentication Service We are going to create an Authentication Service that will handle authentication requests. We are using a service to keep the authentication logic away from our User Model. Let's create this class now: application/modules/storefront/services/Authentication.phpclass Storefront_Service_Authentication{ protected $_authAdapter; protected $_userModel; protected $_auth; public function __construct(Storefront_Model_User $userModel = null) { $this->_userModel = null === $userModel ? new Storefront_Model_User() : $userModel; } public function authenticate($credentials) { $adapter = $this->getAuthAdapter($credentials); $auth = $this->getAuth(); $result = $auth->authenticate($adapter); if (!$result->isValid()) { return false; } $user = $this->_userModel ->getUserByEmail($credentials['email']); $auth->getStorage()->write($user); return true;}public function getAuth(){ if (null === $this->_auth) { $this->_auth = Zend_Auth::getInstance(); } return $this->_auth;}public function getIdentity(){ $auth = $this->getAuth(); if ($auth->hasIdentity()) { return $auth->getIdentity(); } return false;}public function clear(){ $this->getAuth()->clearIdentity();}public function setAuthAdapter(Zend_Auth_Adapter_Interface $adapter){ $this->_authAdapter = $adapter;}public function getAuthAdapter($values){ if (null === $this->_authAdapter) { $authAdapter = new Zend_Auth_Adapter_DbTable( Zend_Db_Table_Abstract::getDefaultAdapter(), 'user', 'email', 'passwd' ); $this->setAuthAdapter($authAdapter); $this->_authAdapter ->setIdentity($values['email']); $this->_authAdapter ->setCredential($values['passwd']); $this->_authAdapter ->setCredentialTreatment( 'SHA1(CONCAT(?,salt))' ); } return $this->_authAdapter; }} The Authentication Service contains the following methods: __constuct: Creates or sets the User Model instance authenticate: Processes the authentication request getAuth: Returns the Zend_Auth instance getIdentity: Returns the stored identity clear: Clears the identity (log out) setAuthAdapter: Sets the authentication adapter to use getAuthAdapter: Returns the authentication adapter The Service is really separated into three areas. They are getting the Zend_Auth instance, configuring the adapter, and authenticating the request using Zend_Auth and the Adapter. To get the Zend_Auth instance, we have the getAuth() method. This method retrieves the singleton Zend_Auth instance and sets it on the $_auth property. It is important to remember that Zend_Auth is a singleton class, meaning that there can only ever be one instance of it. To configure the adapter, we have the getAuthAdapter() method. By default, we are going to use the Zend_Auth_Adapter_DbTable adapter to authenticate the request. However, we can also override this by setting another adapter using the setAuthAdapter() method. This is useful for adding authenticate strategies and testing. The configuration of the DbTable adapter is important here, so let's have a look at that code: $authAdapter = new Zend_Auth_Adapter_DbTable( Zend_Db_Table_Abstract::getDefaultAdapter(), 'user', 'email', 'passwd', 'SHA1(CONCAT(?,salt))');$this->setAuthAdapter($authAdapter);$this->_authAdapter->setIdentity($values['email']);$this->_authAdapter->setCredential($values['passwd']); The Zend_Auth_Adapter_DbTable constructor accepts five parameters. They are database adapter, database table, table name, identity column, and credential treatment. For our adapter, we supply the default database adapter for our table classes using the getDefaultAdapter() method, the user table, the email column, the passwd column, and the encryption and salting SQL for the password. Once we have our configured adapter, we set the identity and credential properties. These will then be used during authentication. To authenticate the request, we use the authenticate method. $adapter = $this->getAuthAdapter($credentials);$auth = $this->getAuth();$result = $auth->authenticate($adapter);if (!$result->isValid()) { return false;}$user = $this->_userModel ->getUserByEmail($credentials['email']);$auth->getStorage()->write($user);return true; Here we first get the configured adapter, get the Zend_Auth instance, and then fetch the result using Zend_Auth's authenticate method, while passing in the configured adapter. We then check that the authentication request was successful using the isValid() method. At this point, we can also choose to handle different kinds of failures using the getCode() method. This will return one of the following constants: Zend_Auth_Result::SUCCESSZend_Auth_Result::FAILUREZend_Auth_Result::FAILURE_IDENTITY_NOT_FOUNDZend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUSZend_Auth_Result::FAILURE_CREDENTIAL_INVALIDZend_Auth_Result::FAILURE_UNCATEGORIZED By using these, we could switch and handle each error in a different way. However, for our purposes, this is not necessary. If the authentication request was successful, we then retrieve a Storefront_Resource_User_Item instance from the User Model and then write this object to Zend_Auth's persistence layer by getting the storage instance using  getStorage() and writing to it using write(). This will then store the user in the session so that we can retrieve the user information throughout the session. Our Authentication Service is now complete, and we can start using it to create a login system for the Storefront.
Read more
  • 0
  • 0
  • 2793

article-image-customizing-document-joomla-15-part-2
Packt
18 Nov 2009
3 min read
Save for later

Customizing the Document with Joomla! 1.5: Part 2

Packt
18 Nov 2009
3 min read
Creating a PDF in a component This recipe explains how to create a PDF view in a Joomla! MVC component. Adding PDF views is a relatively quick process, and it significantly improves the functionality of a component. Getting ready Like any other view format, we must create a new JView subclass to create a PDF view. This should be located in the corresponding view's folder and the file should be named view.pdf.php. For example, for the myview view in the mycomponent component, we create the components/com_mycomponent/views/myview/view.pdf.php file, in which we place the MycomponentViewMyview class, which extends JView. How to do it... The first thing we do is override the display() method in order to change the PDF document. We modify the document using the mutator methods. The first method changes the document title, this is the title normally shown in the title bar of the PDF viewer. $document->setTitle($title); The next method changes the filename. This is especially useful if the user is likely to save the file, as this will be the default name the user is prompted to save the file as. $document->setName($filename); The next method sets the document description, sometimes referred to as the subject. This should only be a very brief description of the document. $document->setDescription($description); The next method sets the document metadata. Currently, only keywords are supported. It is possible to set other metadata, but it will not be used in the document. $document->setMetaData('keywords', $keywords); So far, all of the methods do not print anything to the body of the PDF itself. The next method adds a common header to every page. Note that the header text itself is not formatted. $document->setHeader("My PDF Document TitlenMy Subtitle"); Lastly, we can add content to the main body of the PDF document. We achieve this in the normal way by simply outputting the content. echo 'This is my PDF! '; The outputted data can be formatted using some basic HTML tags. The following tags are supported: Type Tags Format <b>, <u>, <i>, <strong>, <em>, <sup>, <sub>, <small>, <font> Heading <h1>, <h2>, <h3>, <h4>, <h5>, <h6> Indentation <blockquote> Linked <a>, <img> List <ol>, <ul>, <li> Spacing <p>, <br>, <hr> Table <table>, <tr>, <td>, <th>
Read more
  • 0
  • 0
  • 1548

article-image-customizing-document-joomla-15-part-1
Packt
18 Nov 2009
3 min read
Save for later

Customizing the Document with Joomla! 1.5: Part 1

Packt
18 Nov 2009
3 min read
Introduction The classes that extend JDocument are intended for different response formats. The following table describes the classes and their purposes: Class Format Default MIME type Purpose JDocumentError error text/html Display a fatal error JDocumentFeed feed application/rss+xml   or application/atom+xml Syndication feed, RSS, or Atom JDocumentHTML html text/html Default document used for all typical Joomla! responses JDocumentPDF pdf application/pdf Adobe PDF representation JDocumentRAW raw* text/html All other circumstances   So what exactly does the format column represent? When a request is made, Joomla! uses the value of the request variable format to determine which document type to use. We really see this only when we retrieve something other than an HTML document, because this always defaults to html. For example, when we request an article as a PDF, we use a URI similar to this: http://example.org/index.php?option=com_content&view=article&id=5&format=pdf The JDocumentError class is slightly different from the others. We should never really need to interact directly with this. We can, of course, invoke this document indirectly by raising a fatal error. For more information, refer to Error handling and reporting. The Joomla! document object and views in Joomla! MVC components are closely related. For each format that we provide a view, we create a new JView subclass. For example, when we examine the content component, we can see that the article view supports HTML and PDF simply by the presence of the view.html.php and view.pdf.php files. This article also deals with the static JResponse class. This class is used to define the HTTP response, including the HTTP headers. The separation between JResponse and the JDocument object is not always as clear as one would hope. However, this is somewhat inevitable because the two are inextricably linked—the response describes and includes the document output. For example, outputting an HTML response will require the response Content-Type header field to be set accordingly, that is, as text/html. Setting the document title This recipe explains how to set the title of the current document. The exact meaning of title will depend on the type of document. For example, in an HTML document, this is the value encapsulated in the <head> tag. Getting ready Before we do anything, we need the global document object. $document =& JFactory::getDocument(); How to do it... To set the title of the document, we use the JDocument::setTitle() method $document->setTitle('My Unique Title'); If we are outputting an HTML document, this should generate something like this: <title>My Unique Title</title> There's more... Menu items can also define page titles. Thus, the actual title we use should not necessarily be the title of whatever we are viewing. To deal with this, we should use something along these lines: // get the component and page parameters$application =& JFactory::getApplication();$params =& $application->getParams();// get the page title$pageTitle = $params->get('page_title', $defaultTitle);// set the document title$document->setTitle($pageTitle); Setting the document generator This recipe explains how to set the name of the piece of software that generated the page. The exact meaning of generator will depend on the type of document. For example, in an HTML document, this value is used in a tag. Getting ready Before we do anything, we need the global document object. $document =& JFactory::getDocument();
Read more
  • 0
  • 0
  • 2299

article-image-event-delivery-network-oracle-soa-suite-11g-r1
Packt
18 Nov 2009
2 min read
Save for later

Event Delivery Network with Oracle SOA Suite 11g R1

Packt
18 Nov 2009
2 min read
Creating truly decoupled composite SOA applications requires a complete separation of the service consumer and the service provider.This is typically achieved through the use of asynchronous messaging.In an asynchronous messaging pattern, applications can perform in a"fire and forget" mode. This removes the need of an application to know details of the application on the other side. Additionally, it also improves resource utilization as applications are not holding onto resources until the interaction is complete. On the other hand, this introduces complexities of creating and managing message queues and topics. It requires that both the publisher of the message and the consumer use the same messaging technology. Each messaging system also has its own constraints on the types of programming languages and environments that can use the service. In a service-oriented world, this tight coupling to the implementation of the underlying messaging system is at odds with the fundamental requirement of implementation independence. What's needed is a level of abstraction that allows applications to generate an event using business terms and associate a business object in an implementation‑independent form. Oracle SOA Suite 11g addresses this with the introduction of anew feature in the form of the Event Delivery Network. Introducing events The Event Delivery Network (EDN) in Oracle SOA Suite 11g provides a declarative way to use a publish/subscribe model to generate and consume business events without worrying about the underlying message infrastructure. Developers only need to produce or consume events without having to deal with any particular messaging API like JMS, AQ, and MQ, and so on. Consuming an event means expressing an interest in the occurrence of a specific situation,while producing an event means advertising this occurrence. Using the same concepts that are used in Web Service Definition Language (WSDL), EDN uses an XML-based Event Definition Language, which allows you to define the event and its associated,strongly typed data. This definition is then registered with the SOA Infrastructure and is available to all composites to publish or subscribe.   SERVICES MESSAGING EDN WSDL:Standard service interface model JMS API:Application Programming Interface EDL:Event Definition Language XSD:Strong typing Handful of raw types XSD Business-oriented Developer-oriented Business-oriented Wealth of tools Mostly coding tools Fully declarative  
Read more
  • 0
  • 0
  • 2804

article-image-version-management-upk-35-part-1
Packt
18 Nov 2009
12 min read
Save for later

Version Management with UPK 3.5: Part 1

Packt
18 Nov 2009
12 min read
Though this implies multi-user environment client/server setup, this article could still be of use to a single user. If you are a lone developer working in a stand-alone environment (sometimes referred to as a single-user environment), you may want to just skip straight to the section Exporting and importing content, at the end of this article. Sorry, but version management in a stand-alone environment is almost nonexistent, so you're largely on your own. In a client/server environment, all content objects are stored on a central server. Developers create and edit content on their own workstation (typically, a PC), and then send this content to a database on the central server for storage when they have finished with it. This is referred to as checking in a content object. If they subsequently want to change a content object that is on the server, they need to check out the content object, change it, and then check it back in. Checking in a content object When you create a content object in a client/server environment, the content object is stored in your local repository (that is, on the client side of the client/server set-up). It is important to understand that your local repository is located on your local workstation. This means that if you have a content object in your local repository, and then move to another workstation, you will not see your local version of this content object on the new workstation. This is because they are located on the workstation on which you created them or checked them out. You should, therefore, always use the same workstation, or check in all of your content objects before moving to a new workstation. Version Difference In UPK 3.5.1 client/server edition, it is possible to choose a specific location for your local repository. Documents that are checked out to your local repository are identified by a green check-mark () to the left of the object details in the Library. You will need to check these objects in if you want them to be stored in the central library so they will be accessible to other developers. Generally, you should do this whenever you have finished working on a content object. However, there are good reasons for checking in objects more frequently. Your local repository is (typically) in the C: drive of your computer, whereas the central library is on a central server. Unless you are very fastidious about backing up your PC, the chances are that the server is backed up more often than your PC, and is usually backed up automatically. Placing your files in the central library will, therefore, provide greater security in terms of having a backup that you can revert to in the event of a disaster. In addition to this, whenever you check in a content object, a back-up of the previous version is taken. Should you need to back out your changes, you can simply revert to a back-up version of the content object. Related to this, content objects are never really deleted from the server library. Actually, they can be deleted, but only by an Administrator, and typically as part of a cleanup. However, if you delete an object (that has never been checked in to the central server) from your local repository, it is gone. Forever! Finally, it is not unheard of that for a local repository to simply disappear when UPK crashes (which thankfully doesn't happen that often, but it has been known to happen). If this does happen to you, then you may again lose all of the content that you currently have checked out even though the Lost documents view, introduced in UPK 3.5, may save you. For all of these reasons, and more, it is recommended that you check in all of the content that is stored in your local repository to the server at least at the end of each work day. Fortunately, UPK provides a quick way of doing this. If you click the Check In All button (), then all of the content objects that you currently have checked out will be checked in again. The downside of this is that if you do check in all of the documents that you are currently working on at the end of the day, you need to remember which objects these are so that you can check them out again the next day. Before checking the documents in, you can use the All Checked Out view to identify all of the documents that you currently have checked out. You can then print this list, and will know which files you need to check out again the next day. There is one further thing to bear in mind when deciding how often you are going to check in your content objects. As mentioned above, UPK retains every version of a content that has been checked in. These are accessible via the Document History, as we will see later. This means that if you check in a content object ten times, then UPK will retain ten versions of it. This can also make it difficult to identify the "released" versions of the content object in the Document History, should you subsequently need to roll back to the last-released version. The Administrator can clean up the database periodically, but the only way of differentiating between versioned drafts and versioned released content objects is by using the check-in comments. These are explained in the section Step 2: Confirming the scope of the check-in, ahead. UPK Workflow The down-side to checking in content objects that you may not have finished working on is that these objects could be mis-perceived as being final, and actually used or published. In some cases, you may have checked some of them out not because you want to change them, but specifically to stop anyone else from changing them. UPK does not provide you with a simple, automatic solution to these shortcomings, but there is a manual workaround that you can use. This is to use the Workflow properties of the objects. UPK does not have a "real" workflow, it does not have "routing" of content objects based on their status, or automatic email notification of content objects that are due for review, or any of the things that you'd expect from workflow functionality. Of course users can use a Custom View to display content objects according to their Workflow properties, but this is still not really "workflow". If you display the Properties for a content object, by clicking on the Properties button (), the last category of properties (at the end of the Properties list) is the Workflow category. Within this, there are two properties: Assigned To: This property allows you to select (from a drop-down list) the user ID of any developer who is authorized to access the content object. If you select your own user ID in this field, then this is an indication to the other developers that they should not work on this content object themselves. State: This property can be used to specify the current status of a content object. The default values are (blank), Not Started, Draft, In Review, and Final. If you select a status of Draft, then this is an indication that you have not finished with the object. An example of the Properties pane, showing the Workflow category, is in the following screenshot: Of course, these Properties do not provide "hard checks". You still rely on people seeing, and then paying attention to, these Properties but it is a start. Some strong guidance from your Project Manager or Team Leader would help, here. Administrators can add, remove, or rename statuses. It is recommended that a suitable set of statuses are established and defined prior to any development work starting, and that these statuses and their meaning are communicated to all developers. Step 1: Selecting the content objects To select content objects for checking in, carry out the steps described below: In the Library, select the content objects that you want to check in. Normally, you will want to check in specific content objects, so you would only select these. As always, you click on a content object to select it. You can Ctrl-click to select multiple objects, or Shift-click to select a series of objects. You can also select a folder in the Library to check in all of the content objects in that folder. Do not worry if some of the objects that are included in your selection are not checked out; UPK will simply ignore these objects during check-in (it will not "error-out"). You can also check in content objects by selecting them in an Outline Element. Once you have selected all of the content objects that you want to check in, click the Check In button (). Step 2: Confirming the scope of the check-in When you check in one or more specific content objects and have not selected a folder in the Library to check in, UPK will automatically check for any content objects that are related to the object that you are checking in. You will be prompted to check these in, as well. An example of this prompt is shown in the following screenshot: What are related documents? In general, they are Web Pages and Packages used by the content object that you are checking in. For example: If you have created, or changed, an Outline Element (Module or Section) then UPK will automatically select all of the content objects (Modules, Lessons, and Topics) included in this Outline Element, along with any Web Pages used by any of these content objects will be selected as well. If you are checking in a Topic for which you have also created a Web Page that is linked to the Concepts pane, then UPK will automatically select this Web Page as a related document during check-in. If you have created a Glossary and are checking this in, the related documents will be all of the Web Pages for the content definitions used by the Glossary. An example of the Related Documents dialog box is shown in the following screenshot: This list of related documents can be confusing, as it does not match the list that you see in the Related Documents pane for the content object that you are checking in. The list will include Web Pages that have been explicitly linked to from the content object that you are checking, but will not include Glossaries, Templates, Packages, or icons, all of which are listed in the Related Documents pane. This limitation is important to understand. If you checked out, and updated, a Glossary and as a result UPK checked out several content objects during the glossary link regeneration, UPK will not identify all of the Glossary elements (Web Pages) as "related documents". You will need to locate these and check them in separately using custom views. Adding to the confusion, the list of related documents will include all of the explicitly-linked content objects, and not only those objects that are currently checked out to you. Do not worry about this – UPK will only physically check in the content objects that you do have checked out (these are the ones that have your User ID in the Checked Out To column of the Related Documents dialog box). In the example above, we are checking in an Outline Element that contains one Topic, which in turn contains a Web Page. The Topic is checked out, but the Web Page is not. Step 3: Specifying a reason for the change When you check in one or more content objects, you will be prompted to enter a short description of the change, as shown in the following screenshot: The title of the content object that you are checking in is shown above the comment text box. If you are checking in multiple content objects, then this will initially specify the first object and (assuming that you deselect the Apply same comments to all documents option) will specify the next object when you click OK. It then moves on to the next object. In this way, you can provide separate comments for each content object, even though you are checking them all in at the same time. Although it is entirely optional, it is a very good practice to always enter a comment. This comment will appear in the object History, and therefore serves as a useful audit log. Specify a reason for the change by carrying out the steps described below: Enter a suitable comment in the space provided. Note that you do not need to specify the date and time of the change, or your user ID. These are captured automatically by UPK. If you are checking in multiple objects, then UPK will initially suggest applying the same comments to all of the objects. You will see that the Apply same comments to all documents checkbox toward the bottom of the dialog box is selected. If you do not want to use the same comment for all objects that you are checking in, then deselect this checkbox. Now, when you click on OK, the Check In Comment dialog box for the next content object in the check-in selection will be displayed. You can repeat the above processing from Step 3. Once you have entered your description of the change (or reason for the change), click on OK. Once you have specified a comment, and clicked on OK for the last time, then the content objects are checked in, and the Library screen is redisplayed.  
Read more
  • 0
  • 0
  • 1800

article-image-version-management-upk-35-part-2
Packt
18 Nov 2009
5 min read
Save for later

Version Management with UPK 3.5: Part 2

Packt
18 Nov 2009
5 min read
Restoring a deleted document In a client/server environment, content objects are never really deleted; they still exist on the server. Instead, they are removed from the Library folders so that they are not visible within the Library itself. However, they will appear in the Deleted documents view, from where developers can display and restore them. The only way they can be truly deleted is by the Administrator purging them. The advantage of this is that you can always (up until the point that the Administrator has purged it) "un-delete" a content object, if you realize that you deleted it by mistake. To restore (un-delete) a content object, carry out the following steps: From the Library screen, click on the drop-down button for the View field (which will normally indicate Details view) on the View toolbar, and select Deleted documents. A list of all of the content objects that have been deleted (but not purged) is displayed, as shown in the following screenshot: Right-click on the document that you want to restore, and select Restore from the context menu. This is the only option available on this context menu. The document is restored to the Library, and placed back in the folder it was originally deleted from. This location is also shown in the Original location column in the Deleted documents view. This will create another version in the version history for the content object—just as deleting it did. This is shown in the screenshot below: Exporting and importing content If you are working in a stand-alone environment and want to share your developed content with another user, you will need to export it from your Library so they can import it into their Library. If you are a developer working in a client/server environment, it is unlikely that you will need to export your content because all users will be able to access it in the same Library. However, even then there may be a need to export your content, for example: Your company may use a "staged" approach to implementation, whereby all content is developed in a Development library before being migrated to a more secure production environment If you are working with multiple libraries, then it is recommended that you always export/ import folders, and not simply Outline Elements and their contents. This will reduce the possibility of elements that appear in multiple outlines being overwritten on successive imports. You have developed content in one environment (for example, your personal stand-alone environment) and now want to bring it into your company's client/server environment You want to take a copy of everything you have done, as a personal back-up You want to export all of your content before upgrading to a new version of UPK and importing your content into the new version's Library Whatever the reason, UPK makes it easy to export content. In this section we'll look at just how to do this. You would not normally use this approach to export content for translation. Exporting content objects You can use the Export function to export content objects as an archive, publishing options, sound files, and content objects for localization. This section covers exporting content objects as an archive. You can export any type of content object to an archive: Modules, Sections, and Topics, as well as Web Pages and Packages. You can also export any of the objects in the System folder, such as Templates and Publishing Packages. To export a content object, carry out the following steps: In the Library, select on the content objects that you want to export. You can select a single object, or multiple objects of various types. You also have the option of selecting a folder and exporting all of the content within the folder. Select menu option Tools|Export|Documents. The Export dialog box is displayed, as shown in the following screenshot: Click on the Browse button next to the Export content to: field. The Save As dialog box is displayed, as shown below: Enter a suitable file name for your export file in the File name field. Note that UPK will automatically select a file type of Content archives (*.odarc) and add the file prefix .odarc (for OnDemand Archive. Note the reference to OnDemand even though the product is no longer called OnDemand!). Click on Save to confirm the destination folder and file. Back in the Export dialog box, under Selection Options, select whether you want to export only the specific content that you have selected (Export my selection), or the selected content and all related documents (Export my selection and related documents). Related documents are typically content objects such as web pages that are linked to the selected object(s). You can see a list of these objects by clicking the View related documents link. Depending on how you have organized your Library, you should normally export entire folders and their contents, not Outline Elements. Otherwise, you risk missing out objects that are not directly linked into the Outline, such as Packages. You also risk having broken links when you (re)import the content object(s). Click on Export. The selected content objects (and all related documents, if you chose that option) are exported to the specified file. While this is being done, a progress indicator is displayed for the object currently being exported, and the overall percentage complete. The export may be very quick, especially if you are only exporting one or two Topics so don't blink or you'll miss it. An example of the progress indicator is shown below: Once the export is complete, you are returned to the Library. As the content is exported to a single file, you can easily send the file to another Developer for importing into their UPK Library, save it to CD as a back-up, and so on. How to import content objects is described next.
Read more
  • 0
  • 0
  • 2291
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 $19.99/month. Cancel anytime
article-image-user-interaction-and-email-automation-symfony-13-part1
Packt
18 Nov 2009
14 min read
Save for later

User Interaction and Email Automation in Symfony 1.3: Part1

Packt
18 Nov 2009
14 min read
The signup module We want to provide the users with the functionality to enter their name, email address, and how they found our web site. We want all this stored in a database and to have an email automatically sent out to the users thanking them for signing up. To start things off, we must first add some new tables to our existing database schema. The structure of our newsletter table will be straightforward. We will need one table to capture the users' information and a related table that will hold the names of all the places where we advertised our site. I have constructed the following entity relationship diagram to show you a visual relationship of the tables: All the code used in this article can be accessed here. Let's translate this diagram into XML and place it in the config/schema.xml file: <table name="newsletter_adverts" idMethod="native" phpName="NewsletterAds"> <column name="newsletter_adverts_id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true" /> <column name="advertised" type="VARCHAR" size="30" required="true" /> </table> <table name="newsletter_signups" idMethod="native" phpName="NewsletterSignup"> <column name="id" type="INTEGER" required="true" autoIncrement="true" primaryKey="true" /> <column name="first_name" type="VARCHAR" size="20" required="true" /> <column name="surname" type="VARCHAR" size="20" required="true" /> <column name="email" type="VARCHAR" size="100" required="true" /> <column name="activation_key" type="VARCHAR" size="100" required="true" /> <column name="activated" type="BOOLEAN" default="0" required="true" /> <column name="newsletter_adverts_id" type="INTEGER" required="true"/> <foreign-key foreignTable="newsletter_adverts" onDelete="CASCADE"> <reference local="newsletter_adverts_id" foreign="newsletter_adverts_id" /> </foreign-key> <column name="created_at" type="TIMESTAMP" required="true" /> <column name="updated_at" type="TIMESTAMP" required="true" /> </table> We will need to populate the newsletter_adverts table with some test data as well. Therefore, I have also appended the following data to the fixtures.yml file located in the data/fixtures/ directory: NewsletterAds: nsa1: advertised: Internet Search nsa2: advertised: High Street nsa3: advertised: Poster With the database schema and the test data ready to be inserted into the database, we can once again use the Symfony tasks. As we have added two new tables to the schema, we will have to rebuild everything to generate the models using the following command: $/home/timmy/workspace/milkshake>symfony propel:build-all-load --no-confirmation Now we have populated the tables in the database, and the models and forms have been generated for use too. Binding a form to a database table Symfony contains a whole framework just for the development of forms. The forms framework makes building forms easier by applying object-oriented methods to their development. Each form class is based on its related table in the database. This includes the fields, the validators, and the way in which the forms and fields are rendered. A look at the generated base class Rather than starting off with a simple form, we are going to look at the base form class that has already been generated for us as a part of the build task we executed earlier. Because the code is generated, it will be easier for you to see the initial flow of a form. So let's open the base class for the NewsletterSignupForm form. The file is located at lib/form/base/BaseNewsletterSignupForm.class.php: class BaseNewsletterSignupForm extends BaseFormPropel { public function setup() { $this->setWidgets(array( 'id' => new sfWidgetFormInputHidden(), 'first_name' => new sfWidgetFormInput(), 'surname' => new sfWidgetFormInput(), 'email' => new sfWidgetFormInput(), 'activation_key' => new sfWidgetFormInput(), 'activated' => new sfWidgetFormInputCheckbox(), 'newsletter_adverts_id' => new sfWidgetFormPropelChoice (array('model' => 'NewsletterAds', 'add_empty' => false)), 'created_at' => new sfWidgetFormDateTime(), 'updated_at' => new sfWidgetFormDateTime(), )); $this->setValidators(array( 'id' => new sfValidatorPropelChoice(array ('model' => 'NewsletterSignup', 'column' => 'id', 'required' => false)), 'first_name' => new sfValidatorString(array('max_length' => 20)), 'surname' => new sfValidatorString(array('max_length' => 20)), 'email' => new sfValidatorString(array('max_length' => 100)), 'activation_key' => new sfValidatorString(array('max_length' => 100)), 'activated' => new sfValidatorBoolean(), 'newsletter_adverts_id'=> new sfValidatorPropelChoice(array ('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id')), 'created_at' => new sfValidatorDateTime(), 'updated_at' => new sfValidatorDateTime(), )); $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); $this->errorSchema = new sfValidatorErrorSchema ($this->validatorSchema); parent::setup(); } There are five areas in this base class that are worth noting: This base class extends the BaseFormPropel class, which is an empty class. All base classes extend this class, which allows us to add global settings to all our forms. All of the columns in our table are treated as fields in the form, and are referred to as widgets. All of these widgets are then attached to the form by adding them to the setWidgets() method. Looking over the widgets in the array, you will see that they are pretty standard, such as sfWidgetFormInputHidden(), sfWidgetFormInput(). However, there is one widget added that follows the relationship between the newsletter_sigups table and the newsletter_adverts table. It is the sfWidgetFormPropelChoice widget. Because there is a 1:M relation between the tables, the default behavior is to use this widget, which creates an HTML drop-down box and is populated with the values from the newsletter_adverts table. As a part of the attribute set, you will see that it has set the model needed to retrieve the values to NewsletterAds and the newsletter_adverts_id column for the actual values of the drop-down box. All the widgets on the form must be validated by default. To do this, we have to call the setValidators() method and add the validation requirements to each widget. At the moment, the generated validators reflect the attributes of our database as set in the schema. For example, the first_name field in the statement 'first_name' => new sfValidatorString(array('max_length' => 20)) demonstrates that the validator checks if the maximum length is 20. If you remember, in our schema too, the first_name column is set to 20 characters. The final part calls the parent's setup() function. The base class BaseNewsletterSignupForm contains all the components needed to generate the form for us. So let's get the form on a page and take a look at the method to customize it. There are many widgets that Symfony provides for us. You can find the classes for them inside the widget/ directory of your Symfony installation. The Symfony propel task always generates a form class and its corresponding base class. Of course, not all of our tables will need to have a form bound to them. Therefore, delete all the form classes that are not needed. Rendering the form Rendering this basic form requires us to instantiate the form object in the action. Assigning the form object to the global $this variable means that we can pass the form object to the template just like any other variable. So let's start by implementing the newsletter signup module. In your terminal window, execute the generate:module task like this: $/home/timmy/workspace/milkshake>symfony generate:module frontend signup Now we can start with the application logic. Open the action class from apps/frontend/modules/signup/actions/actions.class.php for the signup module and add the following logic inside the index action: public function executeIndex(sfWebRequest $request) { $this->form = new NewsletterSignupForm(); return sfView::SUCCESS; } As I had mentioned earlier, the form class deals with the form validation and rendering. For the time being, we are going to stick to the default layout by allowing the form object to render itself. Using this method initially will allow us to create rapid prototypes. Let's open the apps/frontend/signup/templates/indexSuccess.php template and add the following view logic: <form action="<?php echo url_for('signup/submit') ?>" method="POST"> <table><?php echo $form ?></table> <input type="submit" /> </form> The form class is responsible for rendering of the form elements only. Therefore, we have to include the <form> and submit HTML tags that wrap around the form. Also, the default format of the form is set to 'table'. Again, we must also add the start and end tags of the <table>. At this stage, we would normally be able to view the form in the browser. But doing so will raise a Symfony exception error. The cause of this is that the results retrieved from the newsletter_adverts table are in the form of an array of objects. These results need to populate the select box widget. But in the current format, this is not possible. Therefore, we have to convert each object into its string equivalent. To do this, we need to create a PHP magic function of __toString() in the DAO class NewsletterAds. The DAO class for NewlsetterAds is located at lib/model/NewsletterAds.php just as all of the other models. Here we need to represent each object as its name, which is the value in the advertised column. Remember that we need to add this method to the DAO class as this represents a row within the results, unlike the peer class that represents the entire result set. Let's add the function to the NewsletterAds class as I have done here: class NewsletterAds extends BaseNewsletterAds { public function __toString() { return $this->getAdvertised(); } } We are now ready to view the completed form. In your web browser, enter the URL http://milkshake/frontend_dev.php/signup and you will see the result shown in the following screenshot: As you can see, although the form has been rendered according to our table structure, the fields which we do not want the user to fill in are also included. Of course, we can change this quiet easily. But before we take a look at the layout of the form, let's customize the widgets and widget validators. Now we can begin working on the application logic for submitting the form. Customizing form widgets and validators All of the generated form classes are located in the lib/form and the lib/form/base directories. The latter is where the default generated classes are located, and the former is where the customizable classes are located. This follows the same structure as the models. Each custom form class inherits from its parent. Therefore, we have to override some of the functions to customize the form. Let's customize the widgets and validators for the NewsletterSignupForm. Open the lib/forms/NewsletterSignupForm.class.php file and paste the following code inside the configure() method: //Removed unneeded widgets unset( $this['created_at'], $this['updated_at'], $this['activation_key'], $this['activated'], $this['id'] ); //Set widgets //Modify widgets $this->widgetSchema['first_name'] = new sfWidgetFormInput(); $this->widgetSchema['newsletter_adverts_id'] = new sfWidgetFormPropelChoice(array('model' => 'NewsletterAds', 'add_empty' => true, 'label'=>'Where did you find us?')); $this->widgetSchema['email'] = new sfWidgetFormInput (array('label' => 'Email Address')); //Add validation $this->setValidators(array ('first_name'=> new sfValidatorString(array ('required' => true), array('required' => 'Enter your firstname')), 'surname'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your surname')), 'email'=> new sfValidatorString(array('required' => true), array('invalid' => 'Provide a valid email', 'required' => 'Enter your email')), 'newsletter_adverts_id' => new sfValidatorPropelChoice(array('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id'), array('required' => 'Select where you found us')), )); //Set post validators $this->validatorSchema->setPostValidator( new sfValidatorPropelUnique(array('model' => 'NewsletterSignup', 'column' => array('email')), array('invalid' => 'Email address is already registered')) ); //Set form name $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); //Set the form format $this->widgetSchema->setFormFormatterName('list'); Let's take a closer look at the code. Removing unneeded fields To remove the fields that we do not want to be rendered, we must call the PHP unset() method and pass in the fields to unset. As mentioned earlier, all of the fields that are rendered need a corresponding validator, unless we unset them. Here we do not want the created_at and activation_key fields to be entered by the user. To do so, the unset() method should contain the following code: unset( $this['created_at'], $this['updated_at'], $this['activation_key'], $this['activated'], $this['id'] ); Modifying the form widgets Although it'll be fine to use the remaining widgets as they are, let's have a look at how we can modify them: //Modify widgets $this->widgetSchema['first_name'] = new sfWidgetFormInput(); $this->widgetSchema['newsletter_adverts_id'] = new sfWidgetFormPropelChoice(array('model' => 'AlSignupNewsletterAds', 'add_empty' => true, 'label'=>'Where did you find us?')); $this->widgetSchema['email'] = new sfWidgetFormInput(array('label' => 'Email Address')); There are several types of widgets available, but our form requires only two of them. Here we have used the sfWidgetFormInput() and sfWidgetFormPropelChoice() widgets. Each of these can be initialized with several values. We have initialized the email and newsletter_adverts_id widgets with a label. This basically renders the label field associated to the widget on the form. We do not have to include a label because Symfony adds the label according to the column name. Adding form validators Let's add the validators in a similar way as we have added the widgets: //Add validation $this->setValidators(array( 'first_name'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your firstname')), 'surname'=> new sfValidatorString(array('required' => true), array('required' => 'Enter your surname')), 'email'=> new sfValidatorEmail(array('required' => true), array('invalid' => 'Provide a valid email', 'required' => 'Enter your email')), 'newsletter_adverts_id' => new sfValidatorPropelChoice(array ('model' => 'NewsletterAds', 'column' => 'newsletter_adverts_id'), array('required' => 'Select where you found us')), )); //Set post validators $this->validatorSchema->setPostValidator(new sfValidatorPropelUnique(array('model' => 'NewsletterSignup', 'column' => array('email')), array('invalid' => 'Email address is already registered')) ); Our form will need four different types of validators: sfValidatorString: This checks the validity of a string against a criteria. It takes four arguments—required, trim, min_length, and max_length. SfValidatorEmail: This validates the input against the pattern of an email address. SfValidatorPropelChoice: It validates the value with the values in the newsletter_adverts table. It needs the model and column that are to be used.   SfValidatorPropelUnique: Again, this validator checks the value against the values in a given table column for uniqueness. In our case, we want to use the NewsletterSignup model to test if the email column is unique. As mentioned earlier, all the fields must have a validator. Although it's not recommended, you can allow extra parameters to be passed in. To achieve this, there are two steps: You must disable the default option of having all fields validated by $this->validatorSchema->setOption('allow_extra_fields', true). Although the above step allows the values to bypass validation, they will be filtered out of the results. To prevent this, you will have to set $this->validatorSchema->setOption('filter_extra_fields', false). Form naming convention and setting its style The final part we added is the naming convention for the HTML attributes and the style in which we want the form rendered. The HTML output will use our naming convention. For example, in the following code, we have set the convention to newsletter_signup[fieldname] for each input field's name. //Set form name $this->widgetSchema->setNameFormat('newsletter_signup[%s]'); //Set the form format $this->widgetSchema->setFormFormatterName('list'); Two formats are shipped with Symfony that we can use to render our form. We can either render it in an HTML table or an unordered list. As we have seen, the default is an HTML table. But by setting this as list, the form is now rendered as an unordered HTML list, just like the following screenshot. (Of course, I had to replace the <table> tags with the <ul> tags.)
Read more
  • 0
  • 0
  • 2207

article-image-keeping-extensions-secure-joomla-15-part-2
Packt
18 Nov 2009
15 min read
Save for later

Keeping Extensions Secure with Joomla! 1.5: Part 2

Packt
18 Nov 2009
15 min read
Making a directory path safe When we poke around in the server filesystem, we want to make sure that we know what we are doing. If we are dealing with directories, Joomla! provides us with some easy ways to make sure that those directories are being safely referenced. Failure to properly sanitize directory paths can lead to major security vulnerabilities. For example, if we were attempting to remove a directory, a security vulnerability could allow the deletion of the completely wrong resource! For more information about external control of paths, refer to CWE-73. A directory path is a URL to a directory. A directory path does not include a file. To safely manage a path to a file, refer to the next recipe, Making a path safe. How to do it... The static JFolder class, which is a part of the joomla.filesystem library, provides us with all sorts of useful methods for working with folders. To use the class, we must import it. jimport('joomla.filesystem.folder'); This bit is nice and easy. We use the JFolder::makeSafe() method and pass the name of the folder we want to sanitize. This method returns a string that can be used to interact safely with a folder. // make the directory path safe$safeDirPath = JFolder::makeSafe($unsafeDirPath); The one downside of JFolder::makeSafe() is that it assumes that the directory separators are correctly defined in the original string. For example, while running on a *nix system, if the string contained Windows-style backslashes instead of *nix style forward slashes, those slashes would be stripped. We can use the static JPath class to overcome this, as follows: // import JPath and JFolderjimport('joomla.filesystem.path');jimport('joomla.filesystem.folder');// clean the path$cleanDirPath = JPath::clean($unsafeDirPath);// make the directory path safe$safeDirPath = JFolder::makeSafe($cleanDirPath); This time we have included the JPath::clean() method prior to making the path safe. For more information about JPath, refer to the next recipe, Making a path safe. Directory separators The correct way to add directory separators in Joomla! is to use the DS constant. For example, the path to bar from foo is expressed as 'foo' . DS . 'bar'. How it works... What exactly does the JFolder::makeSafe() method do to guarantee that the directory path is safe? It strips out any characters or character sequences that are seen as posing potential security risks. The following list describes the characters that are considered safe: Alphanumeric (a-z, A-Z, and 0-9) Colon Dash Directory separators—the exact character sequence will depend on the environment Space Underscore The following table shows some examples of input and output strings from the JFolder::makeSafe() and JPath::clean() methods running on a Windows system (DS == ''). This is intended to show why using the two together can be preferable. If we choose to use the two together, order of usage is important. We should always use the JPath::clean() method first and the JFolder::makeSafe() method next. Although, as the fourth example shows, sometimes it can be worth cleaning a second time, which means clean, make safe, and clean.   Original   Clean   Safe   Clean and safe   foobar   foobar   foobar   foobar   //foo/bar   foobar   foobar   foobar   foo bar   foo bar   foo20bar   foo20bar   foo bar/..   foo bar..   foo bar   foo bar   /foo"   foo"   foodel   foodel   del *.*   del *.*       See also For information about safely dealing with files, refer to the previous recipe, Making a filename safe. For information about dealing with paths, please refer to the next recipe, Making a path safe. Making a path safe This recipe is similar to the previous two recipes, Making a filename safe and Making a directory path safe. This recipe differs in that it is for a complete path, normally to a file. There is a whole raft of security issues associated with processing paths. The following list identifies some of the more common issues we need to be aware of: CWE-22: Path traversal CWE-73: External control of filename or path CWE-98: Insufficient control of filename for include/require statement in PHP program (aka 'PHP file inclusion') CWE-434: Unrestricted file upload All of these vulnerabilities can have serious consequences, which should not be overlooked. For example, a malicious user could upload a destructively tailored script file and then execute it. Luckily, Joomla! provides us with some easy ways to reduce the risks associated with these potential weaknesses. Getting ready Before we delve into some of the complex ways of safely dealing with paths, let's start small. If we browse a basic installation of Joomla!, we will discover a large number of empty index.html files. These files prevent directory listings. Most web servers automatically generate directory listings if we visit a directory in which there are no index files. We should always add a copy of the empty index.html file to every directory in our extension. The use of empty index.html files provides a form of security through obscurity. This is only intended to be a very basic safeguard and should never be relied on for complete protection. For more information, refer to CWE-656. The second thing we should do is ensure that all of our PHP files can only be executed if the _JEXEC constant has been defined. This is used to make sure that the file has been executed from within Joomla!, that is, make sure it is not being used as a standalone script or is included from a script other than Joomla! // Check file executed from within Joomla!defined('_JEXEC') or die('Restricted access'); How to do it... If we are retrieving a path value from a request variable, we can use the PATH type. This type is not entirely what it seems, as a PATH type cannot be an absolute path and cannot reference hidden files and folders. This means it cannot start with any form of directory separator, as it would in a *nix environment, or a drive identifier, as it would in a Windows environment. It also means that none of the folder names or the optional filename can start with a period (a *nix hidden file/folder). If a value does not reach these criteria, the return value will be null. Therefore, it is very important to consider the suitability of the PATH type before opting to use it. // get the value of myPath from the request$myPath = JRequest::getVar('myPath', 'default', 'REQUEST', 'PATH'); On its own, the PATH type does not really constitute a security measure. For example, it does not protect against the path traversal CWE-22. Once we have our path variable, it is time to clean it. Cleaning is done in Joomla! using the static JPath class. Cleaning resolves any issues with directory separators. The exact process depends on the filesystem directory separator, for example a back or forward slash. The point is that if the directory separators in the string are incorrect, they are corrected as necessary. // import the JPath classjimport('joomla.filesystem.path');// clean $myPath$myPath = JPath::clean($myPath); Like the PATH type, a cleaned path does not really constitute a security measure. For example, it too does not protect against path traversal CWE-22. OK, we've heard enough about not constituting a security measure. Now it's time to overcome that problem. The static JPath class includes the JPath::check() method, which checks for path traversal and also that the path is within the Joomla! installation. The only constraint is that the method can only deal with absolute paths. Remember the PATH type used in JRequest can only cope with relative paths. Therefore, if we use the PATH type, we must convert it to an absolute path before using the JPath::check() method. // check for path traversal and snoopingJPath::check($myPath); The odd thing about this method is that we don't really do anything with the result! There is a very good reason for this. If the check fails, Joomla! will exit and display a suitable error message. In some instances, this may not be appropriate. Unfortunately, there is nothing we can do to prevent this. Therefore, if we want to avoid this we will have to check the path ourselves. Generally speaking, if a path fails the check, it is likely that an attack has been attempted. For that reason, exiting Joomla! is probably the most suitable response. However, the JPath::check() method does have one serious limitation. It only checks for snooping outside of the Joomla! root directory. We can manually check that we are only looking in a specified area in the Joomla! installation. // create path which must be the root of the directory$safePath = JPATH_COMPONENT . DS . 'safeFolder' . DS;// check for snooping outside of $safePathif (strpos($myPath, $safePath) !== 0) { JError::raiseError(20, 'Snooping out of bounds'); jexit();}// check for file traversaljimport('joomla.filesystem.path');JPath::check($directory); Essentially, this only ensures that the start of the $myPath string is equivalent to $safePath. We deal with failures in the same way as the JPath::check() method. Notice that we still use the JPath::check() method because we can still effectively use this method to check for file traversal. See also The previous two recipes, Making a filename safe and Making a directory path safe, discuss how to work safely with filenames and paths. We can also use filesystem permissions to secure files and folders. Safely retrieving request data Almost 99% of the time, security vulnerabilities in PHP applications such as Joomla! are caused by inadequate input parsing and validation CWE-20. We access request data in Joomla! using the static JRequest class. Built into this class is the ability to cast values to specific types and to mask data. We cannot rely solely on JRequest to ensure that incoming data is safe. This is where validation comes into play. Input data always has definable constraints. For example, we might define an entity identifier as a positive integer no less than 1 and no greater than 4294967295 (the maximum value for an unsigned MySQL INT). If we can define the constraints, we can also check that the input adheres to the constraints. Validating strings tend to be more complex. This is because strings are highly versatile and can contain many different characters. One thing we should always bear in mind when dealing with string validation is the effect of different character encodings. Joomla! 1.5 is UTF-8 compliant. UTF-8 is a Unicode variable-size multibyte character encoding that enables the encoding of many different alphabets and symbols that would otherwise be unavailable. As PHP is not UTF-8 aware, we should always use the static JString methods instead of the PHP string functions when dealing with UTF-8 strings. Getting ready Prior to doing anything, it is worth defining and documenting the boundaries of all the input that we use in our extension. This may include value ranges and formats. This can be a lengthy task, but it will help to ensure that our extension is secure. How to do it... The most important thing we must do when accessing request data is to use JRequest. Even if we just want the raw values, JRequest forms an important part of the Joomla! framework. Even before we get a chance to execute any code in our extension, JRequest will have already performed vital security work in an attempt to prevent global variable injection, CWE-471. So where do we begin? We start with the simple JRequest::getVar() method. This method is used to safely get at the request data. There are five parameters, of which only the first is required. The following example shows how we use the first three of these parameters: // gets the value of name$value = JRequest::getVar('nameOfVar');// gets the value of name,// if name is not specified returns defaultValue$value = JRequest::getVar('nameOfVar', 'defaultValue');// gets the value of name,// if name is not specified returns defaultValue// name is retrieved from the GET request data$value = JRequest::getVar('nameOfVar', 'defaultValue', 'GET'); The third parameter can be any of the following values:   Value Description COOKIE HTTP Cookies ENV Environment variables FILES Uploaded file details GET HTTP GET variables POST HTTP POST variables REQUEST Combination of GET, POST, and COOKIE; this is the default SERVER Server and environment variables In most instances, REQUEST (the default) should be sufficient. The only time we need to use GET and POST explicitly is when we are expecting a request to use a specific HTTP method. For added security, we can restrict a request to a certain HTTP method using the static JRequest::getMethod() method, as shown in the following example: if (JRequest::getMethod() != 'GET') { jexit('UNEXPECTED REQUEST METHOD');} There's more... The following two subsections discuss the last two parameters, $type and $mask. It is in the last two parameters where the security benefits of JRequest become very apparent. Casting Strictly speaking, casting is not an accurate description of the fourth JRequest::getVar() parameter. The types that we can cast to are not the types that we would use to describe a variable. For example, WORD is not a PHP or Joomla! type, but it is an available option. The following example extracts an integer representation of the GET request value, nameOfVar: // gets the value of nameOfVar,// if nameOfVar is not specified returns 0// name is retrieved from the GET request data// casts the return value as an integer$int = JRequest::getVar('nameOfVar', 0, 'GET', 'INT'); Hey presto! We have a safe value that we know is an integer. Had we not included the parameter to cast the value, we would not have been able to guarantee that $int was in fact an integer. Of course, we could have used the PHP intval() function or cast the value ourselves using (int). Not all of the types we can cast to are as simple as an integer; for example, ALNUM is used to strip non-alphanumeric characters from a string value. JRequest also provides alias methods that allow us to achieve the same thing, but with less code. For example, we can quickly extract an integer. The following example is the same as the previous example: $int = JRequest::getInt('nameOfVar', 0); The following table describes all of the types we can cast to using JRequest. Note that only the most commonly used types have alias methods. Type   Description   Alias   DEFAULT   Aggressive cleaning occurs to remove all detected code elements     ALNUM   Alphanumeric string; strips all non-ASCII letters and numbers     ARRAY   Force array cast     BASE64   Base64 string; strips all non Base64 characters, which is useful for passing encoded data in a URL, for example a return URL     BOOL or BOOLEAN   Force Boolean cast   getBool()   CMD   Command; strips all non-alphanumeric, underscore, period, and dash characters, which is ideal for values such as task   getCmd()   FLOAT or DOUBLE   Floating point number   getFloat()   INT or INTEGER   Whole number   getInt()   PATH   Filesystem path; used to identify a resource in a filesystem, for example the path to an image to use as a logo (relative paths only)     STRING   String; often used with a mask to clean the data   getString()   USERNAME   Username; strips characters unsuitable for use in a username, including non-printing characters (for example a backspace), angled brackets, double and single quotation marks, percent signs, and ampersands     WORD   Word; strips all non-alpha and underscore characters   getWord()     By default, the most aggressive mask is applied. This will remove code, such as HTML and JavaScript, from the data. The next section describes how we use masks. Masking strings The fifth JRequest::getVar() parameter defines a mask. Masks are used in JRequest to define what is and isn't allowed in a string value. By default no masking is applied, which means that very aggressive security measures are taken to ensure that the incoming data is as safe as possible. While this is useful, it is not always appropriate. For example, the core component com_content allows users to enter HTML data in an article. Without a mask, this information would be stripped from the request data. There are three constants we can use to easily define the mask we want to apply. These are JREQUEST_NOTRIM, JREQUEST_ALLOWRAW, and JREQUEST_ALLOWHTML. The following example shows how we can use these: // using getVar$string = JRequest::getVar('nameOfVar', 'default', 'REQUEST', 'STRING', JREQUEST_ALLOWRAW);// using getString alias$string = JRequest::getString('nameOfVar', 'default', 'REQUEST', JREQUEST_ALLOWRAW); The following examples show how the output varies depending on the mask that we apply: # Original input value 1 <p>Paragraph <a onClick="alert('foobar');">link</a></p> 2 CSS <link type="text/css", href="http://somewhere/nasty.css" /> 3 space at front of input 4 &ltp&gtPara&lt/p&gt # Output value (No mask) 1 Paragraph link 2 CSS 3 space at front of input 4 &ltp&gtPara&lt/p&gt    
Read more
  • 0
  • 0
  • 1174

article-image-user-interaction-and-email-automation-symfony-13-part2
Packt
18 Nov 2009
8 min read
Save for later

User Interaction and Email Automation in Symfony 1.3: Part2

Packt
18 Nov 2009
8 min read
Automated email responses Symfony comes with a default mailer library that is based on Swift Mailer 4, the detailed documentation is available from their web site at http://swiftmailer.org. After a user has signed up to our mailing list, we would like an email verification to be sent to the user's email address. This will inform the user that he/she has signed up, and will also ask him or her to activate their subscription. To use the library, we have to complete the following three steps: Store the mailing settings in the application settings file. Add the application logic to the action. Create the email template. Adding the mailer settings to the application Just like all the previous settings, we should add all the settings for sending emails to the module.yml file for the signup module. This will make it easier to implement any modifications required later. Initially, we should set variables like the email subject, the from name, the from address, and whether we want to send out emails within the dev environment. I have added the following items to our signup module's setting file, apps/frontend/config/module.yml: dev: mailer_deliver: true all: mailer_deliver: true mailer_subject: Milkshake Newsletter mailer_from_name: Tim mailer_from_email: no-reply@milkshake All of the settings can be contained under the all label. However, you can see that I have introduced a new label called dev. These labels represent the environments, and we have just added a specific variable to the dev environment. This setting will allow us to eventually turn off the sending of emails while in the dev environment. Creating the application logic Triggering the email should occur after the user's details have been saved to the database. To demonstrate this, I have added the highlighted amends to the submit action in the apps/frontend/modules/signup/actions/actions.class.php file, as shown in the following code: public function executeSubmit(sfWebRequest $request) { $this->form = new NewsletterSignupForm(); if ($request->isMethod('post') && $this->form-> bindAndSave($request->getParameter($this->form-> getName()))) { //Include the swift lib require_once('lib/vendor/swift-mailer/lib/swift_init.php'); try{ //Sendmail $transport = Swift_SendmailTransport::newInstance(); $mailBody = $this->getPartial('activationEmail', array('name' => $this->form->getValue('first_name'))); $mailer = Swift_Mailer::newInstance($transport); $message = Swift_Message::newInstance(); $message->setSubject(sfConfig::get('app_mailer_subject')); $message->setFrom(array(sfConfig:: get('app_mailer_from_email') => sfConfig::get('app_mailer_from_name'))); $message->setTo(array($this->form->getValue('email')=> $this-> form->getValue('first_name'))); $message->setBody($mailBody, 'text/html'); if(sfConfig::get('app_mailer_deliver')) { $result = $mailer->send($message); } } catch(Exception $e) { var_dump($e); exit; } $this->redirect('@signup'); } //Use the index template as it contains the form $this->setTemplate('index'); } Symfony comes with a sfMailer class that extends Swift_Mailer. To send mails you could simply implement the following Symfony method: $this->getMailer()->composeAndSend('from@example.com', 'to@example.com', 'Subject', 'Body'); Let's walk through the process: Instantiate the Swift Mailer. Retrieve the email template (which we will create next) using the $this->getPartial('activationEmail', array('name' => $this->form->getValue('first_name'))) method. Breaking this down, the function itself retrieves a partial template. The first argument is the name of the template to retrieve (that is activationEmail in our example) which, if you remember, means that the template will be called _activationEmail.php. The next argument is an array that contains variables related to the partial template. Here, I have set a name variable. The value for the name is important. Notice how I have used the value within the form object to retrieve the first_name value. This is because we know that these values have been cleaned and are safe to use. Set the subject, from, to, and the body items. These functions are Swift Mailer specific: setSubject(): It takes a string as an argument for the subject setFrom(): It takes the name and the mailing address setTo(): It takes the name and the mailing address setBody(): It takes the email body and mime type. Here we passed in our template and set the email to text/html Finally we send the email. There are more methods in Swift Mailer. Check out the documentation on the Swift Mailer web site (http://swiftmailer.org/). The partial email template Lastly, we need to create a partial template that will be used in the email body. In the templates folder of the signup module, create a file called _activationEmail.php and add the following code to it: Hi <?php echo $name; ?>, <br /><br /> Thank you for signing up to our newsletter. <br /><br /> Thank you, <br /> <strong>The Team</strong> The partial is no different from a regular template. We could have opted to pass on the body as a string, but using the template keeps our code uniform. Our signup process now incorporates the functionality to send an email. The purpose of this example is to show you how to send an automated email using a third-party library. For a real application, you should most certainly implement a two-phase option wherein the user must verify his or her action. Flashing temporary values Sometimes it is necessary to set a temporary variable for one request, or make a variable available to another action after forwarding but before having to delete the variable. Symfony provides this level of functionality within the sfUser object known as a flash variable. Once a flash variable has been set, it lasts until the end of the overall request before it is automatically destroyed. Setting and getting a flash attribute is managed through two of the sfUser methods. Also, you can test for a flash variable's existence using the third method of the methods listed here: $this->getUser()->setFlash($name, $value, $persist = true) $this->getUser()->getFlash($name) $this->getUser()->hasFlash($name) Although a flash variable will be available by default when a request is forwarded to another action, setting the argument to false will delete the flash variable before it is forwarded. To demonstrate how useful flash variables can be, let's readdress the signup form. After a user submits the signup form, the form is redisplayed. I further mentioned that you could create another action to handle a 'thank you' template. However, by using a flash variable we will not have to do so. As a part of the application logic for the form submission, we can set a flash variable. Then after the action redirects the request, the template can test whether there is a flash variable set. If there is one, the template should show a message rather than the form. Let's add the $this->getUser()->setFlash() function to the submit action in the apps/frontend/modules/signup/actions/actions.class.php file: //Include the swift lib require_once('lib/vendor/swift-mailer/lib/swift_init.php'); //set Flash $this->getUser()->setFlash('Form', 'completed'); try{ I have added the flash variable just under the require_once() statement. After the user has submitted a valid form, this flash variable will be set with the name of the Form and have a value completed. Next, we need to address the template logic. The template needs to check whether a flash variable called Form is set. If it is not set, the template shows the form. Otherwise it shows a thank you message. This is implemented using the following code: <?php if(!$sf_user->hasFlash('Form')): ?> <form action="<?php echo url_for('@signup_submit') ?>" method="post" name="Newsletter"> <div style="height: 30px;"> <div style="width: 150px; float: left"> <?php echo $form['first_name']->renderLabel() ?></div> <?php echo $form['first_name']->render(($form['first_name']-> hasError())? array('class'=>'boxError'): array ('class'=>'box')) ?> <?php echo ($form['first_name']->hasError())? ' <span class="errorMessage">* '.$form['first_name']->getError(). '</span>': '' ?> <div style="clear: both"></div> </div> .... </form> <?php else: ?><h1>Thank you</h1>You are now signed up.<?php endif ?> The form is now wrapped inside an if/else block. Accessing the flash variables from a template is done through $sf_user. To test if the variable has been set, I have used the hasFlash() method, $sf_user->hasFlash('Form'). The else part of the statement contains the text rather than the form. Now if you submit your form, you will see the result as shown in the following screenshot: We have now implemented an entire module for a user to sign up for our newsletter. Wouldn't it be really good if we could add this module to another application without all the copying, pasting, and fixing?
Read more
  • 0
  • 0
  • 8654

article-image-keeping-extensions-secure-joomla-15-part-1
Packt
18 Nov 2009
7 min read
Save for later

Keeping Extensions Secure with Joomla! 1.5: Part 1

Packt
18 Nov 2009
7 min read
Introduction There's no such thing as a completely secure system. No matter how many precautions we take and how many times we verify our design and implementation, we will never be able to guarantee that we have created a truly secure Joomla! extension. Why not? This is because it is not possible to be prepared for every potential vulnerability. Common Weakness Enumeration (CWE) is a project dedicated to generating a formal categorization and identification system of security vulnerabilities. CWE published a list of the top 25 security weaknesses, which were selected on the basis of their frequency and consequences. This list includes some of the most publicized security weaknesses, such as code injection (CWE-94) and XSS (CWE-79). When considering the security of our extensions, this list can prove useful. For information about common programming mistakes that lead to security vulnerabilities, refer to http://cwe.mitre.org/top25/. This article includes references to the CWE weaknesses. These references are in the form of CWE IDs, that is, CWE-n. For information about a weakness, simply Search By ID on the CWE web site. These references are intended to help us better understand the weaknesses, the risks associated with the weaknesses, how the risks can be reduced using the Joomla! framework, and the suggested CWE mitigations. Something we should consider is the ramifications of security flaws. Whichever way we look at this, the answer always involves financial loss. This is true even of non-profit organizations. If a web site is attacked and the attacker managed to completely obliterate all the data held on that web site, it will cost the owner's time to restore a backup or replace the data. OK, it may not seem like a financial loss because it's non profit. It is a wastage of time if the web site owner spends two hours to restore his or her data, as those two hours could have been used elsewhere. For commercial web sites, the potential for financial loss is far more obvious. If we use a bank as an example, a security flaw could enable an attacker to transfer money from the bank to his or her own (probably untraceable) account. In 2004, the Internet bank Cahoot suffered a security flaw enabling any existing customer access to other customers' accounts. Cahoot did not suffer any obvious financial loss from the security flaw and they claimed there was no risk of financial loss. However, the customers' confidence in Cahoot was inevitably lost. This loss of confidence and bad press will certainly have affected Cahoot in some way. For example, some potential customers may have decided to open an account with a rival bank because of concerns over how secure their savings would, or would not, be with Cahoot. For more information, refer to http://news.bbc.co.uk/1/hi/business/3984845.stm. From the perspective of an extension developer, we should reflect on our moral duty and our liability. Disclaimers, especially for commercial software, do not relinquish us of legal responsibility. We should always try to avoid any form of litigation, and I'm not suggesting that we run to Mexico or our closest safe haven. We should take a holistic approach to security. We need a complete view of how the system works and of the various elements that need to be secure. Security should be built into our extension from the requirements gathering stage through to the ongoing system maintenance. How we do that depends on how we are managing our project and what the security implications of our extension are. For example, a shopping cart component with credit card processing facilities will require far greater attention to security than a content plugin that converts the occurrences of :) to smiley face images. Irrespective of the way we choose to manage the risks of weaknesses, we should always document how we are circumventing security threats. Doing so will make it easier to maintain our extension without introducing vulnerabilities. Documentation also provides us with proof of prudent risk management, which can be useful should we ever be accused of failing to adequately manage the security risks associated with our software. This is all starting to sound like a lot of work! This brings us back to the ramifications of vulnerabilities. If on the one hand, it costs us one extra month of development time to produce a piece of near-secure software. And on the other hand, it costs us two months to patch a non-secure piece of software and an incalculable amount of damage to our reputation. It is clear which route we should favor! Packt Publishing offers a book that deals specifically with Joomla! security. For more information, refer to http://www.packtpub.com/joomla-web-security-guide/. Writing SQL safe queries SQL injection is probably the most high profile of all malicious web attacks. The effects of an SQL injection attack can be devastating and wide ranging. Whereas some of the more strategic attacks may simply be aimed at gaining access, others may intend on bringing about total disruption and even destruction. Some of the most prestigious organizations in the world have found themselves dealing with the effects of SQL injection attacks. For example, in August 2007 the United Nations web site was defaced as a result of an SQL injection vulnerability. More information can be found at http://news.bbc.co.uk/1/hi/technology/6943385.stm. Dealing with the effects of an SQL injection attack is one thing, but preventing them is quite another. This recipe explains how we can ensure that our queries are safe from attack by utilizing the Joomla! framework. For more information about SQL injection, refer to CWE-89. Getting ready The first thing we need is the database handler. There is nothing special here, just the usual Joomla! code as follows: $db =& JFactory::getDBO(); How to do it... There are two aspects of a query that require special attention: Identifiers and names Literal values The JDatabase::nameQuote() method is used to safely represent identifiers and names. We will start with an easy example, a name that consists of a single identifier. $name = $db->nameQuote('columnIdentifier'); We must take care when dealing with multiple-part names (that is, names that include more than one identifier separated by a period). If we attempt to do the same thing with the name tableIdentifier.columnIdentifier, we won't get the expected result! Instead, we would have to do the following: // prepare identifiers$tableIdentifier = $db->nameQuote('tableIdentifier');$columnIdentifier = $db->nameQuote('columnIdentifier');// create name$name = "$tableIdentifier.$columnIdentifier"; Avoid hardcoding encapsulation Instead of using the JDatabase::nameQuote() method, it can be tempting to do this: $sql = 'SELECT * FROM `#__foobar_groups` AS `group`'. This is OK as it works. But the query is now tightly coupled with the database system, making it difficult to employ an alternative database system. Now we will take a look at how to deal with literal values. Let's start with strings. In MySQL, strings are encapsulated in double or single quotes. This makes the process of dealing with strings seem extremely simple. Unfortunately, this would be an oversight. Strings can contain any character, including the type of quotes we use to encapsulate them. Therefore, it is also necessary to escape strings. We do all of this using the JDatabase::Quote() method as follows: $tableIdentifier = $db->nameQuote('tableIdentifier');$columnIdentifier = $db->nameQuote('columnIdentifier');$sql = "SELECT * FROM $tableIdentifier " . "WHERE $columnIdentifier " . ' = ' . $db->Quote("How's the recipebook going?"); The JDatabase::Quote() method essentially does the following. The exact output will depend on the database handler. However, most databases escape and encapsulate strings in pretty much the same way.   Original Quoted How's the recipebook going? 'How's the recipebook going?'
Read more
  • 0
  • 0
  • 1451
article-image-setting-your-moodle-grade-book
Packt
17 Nov 2009
3 min read
Save for later

Setting up your Moodle Grade Book

Packt
17 Nov 2009
3 min read
Set your classes up with no effort on your part Why would you need to "set your classes up"? Surely if the course is enroll able, your students can just enter and take part in all the activities? They can indeed, but unless you put a password (enrolment key) on the course then any students on your Moodle could enroll and take a look around. That might be fine if you have an "open door" policy to your course – but in the real world, I wouldn't expect extra students to come in and join my Advanced Level French class, and so the same might apply in Moodle.  Likewise, although my colleague and I share teaching resources and experiences, we don't actually mark each other's students' work – we have separate grade books. The same should apply to Moodle. If more than one class is sharing a Moodle course, it’s important that the classes are on different pages of the grade book rather than everyone all listed together. So how do we do this without having manually to add every single student?  If your Moodle admin hasn't done this for you already, then log in to your course and click on Groups in the course administration block: Now click on the button Create group as in the next screenshot: Write the name of your class in the Group Name box, as it might say in their timetable for example. If you wish to put a description of this class you may do so, but it isn’t necessary. Scroll down to the Enrolment key box and enter a password for this class only. (Hint: it might be easier for them to recall if you make it the name of their class) If you click Unmask you will be able to see what you are typing: Click the Save changes button, and you will be returned to the Groups page where you will see your class with (0) next to its name. That's telling you there are no students in there yet! Repeat the process with all the other classes sharing your Moodle course. Your Groups page might end up like this: And finally… click to go back to your main course page and then, in the course administration block, click on Settings. In the page that comes up next, set Group mode to Visible or Separate and in  Availability  set another password (enrollment key). This enrollment key never gets used! It is simply there to keep unwanted students out! It doesn’t matter what you set it to or even whether you yourself remember it or not.
Read more
  • 0
  • 0
  • 1701

article-image-providing-context-using-custom-text-upk-35
Packt
17 Nov 2009
10 min read
Save for later

Providing context using Custom Text in UPK 3.5

Packt
17 Nov 2009
10 min read
The Start Frame You may recall that the very first Frame in a Topic is the Start Frame. This is sometimes referred to as the Introduction frame, which is largely a throwback to OnDemand, as we shall see shortly. As this Frame is the first thing that a trainee will see when they carry out a Topic, coupled with the fact that this Frame is a "non-action" Frame, in that the user does not need to actively do anything (other than pressing Enter to continue), it is a good place to provide some additional information to the trainee. The first thing that you should explain in the Introduction frame is exactly what the trainee will be learning in the exercise. Certainly the title of the Topic should give them a clue, but this is not really detailed enough. A good source of information for this is the learning objectives of the course for which this exercise has been built, or the competencies, depending on your curriculum development process. For our sample exercise, we could use the bubble shown in the following screenshot: This is a good start, but we can do more. It is always useful, with training exercises, to use realistic business scenarios to explain what the trainee is doing, to put the keystrokes and mouse-clicks into a business context. Trainees are much more likely to remember information to which they can relate. Consider telling a story and walking the trainees through that story as they carry out the exercise. Although it is a fairly spurious example, we will continue with our sample exercise. Here, we could use the bubble shown in the following screenshot: So now the trainee has a good idea of what they will learn, and they have an example that they can relate to. The text is also directed at the trainee, so the trainee will feel actively involved. There is another strong argument for including a scenario in the form shown above. Consider the case where you are providing training for users in multiple locations (possibly countries) or departments, each of which has its own set of customers, products, and vendors. Users will always want to see exercises using their data: orders for their products, placed at their location, and so on. To keep everyone happy, you would need to develop a separate, customized Topic for each location or user group. If the basic process (and, most importantly for us, the Actions in the recording) is the same in each case, this is clearly inefficient. However, if you create a scenario, and say something like "You are a Customer Service Representative in the Tampa Service Center. Customer SunCo has phoned through an order for 1,000 gallons of regular gasoline. You need to record this order in the system so that it can be fulfilled by Fuel Services." then trainees who are not at the Tampa Service Center will at least understand that this is role play. It is make believe, and they shouldn't be concerned that they don't see products that they don't supply at their own location. So set the scene with a scenario in the Introduction pane, and then build on this throughout the exercise. Introduction Text: Version differences At this point, it is worth highlighting some key differences between the way the Introduction pane was handled prior to UPK 3.5, and the way that the Introduction pane is used in UPK 3.5. If you open an Outline Element in the Outline Editor, and select a Topic in the navigation tree, you will see that the lower-right portion of the screen is labeled Introduction, as shown in the following screenshot: The Outline Editor is used to organize content objects into the structure that the trainee will see. However, if we look at the published version of the Outline above, we will see the following: Where has the Introduction Frame gone? Put simply. UPK 3.5 does not display the Introduction Frame in the Player any more. Users of UPK 2.x or OnDemand 9.x will recall that the Introduction Frame certainly used to be displayed, as shown in the following screenshot, which is taken from OnDemand 9.1.5: Quite why Oracle decided to change the way the Introduction pane is displayed (or not) in version 3.5 is a mystery (maybe they felt that with all that screen space required for the new Oracle branding, there just wasn't the space left to include the Introduction any more). However, it does have some important implications on the way that we are planning on using it. This is because the effect of a publishing option associated with the Introduction pane has changed significantly. In the Publishing Wizard, the options for the Player package include an option to Show introduction text. In OnDemand version 9.1, this option determined whether the Introduction text appeared in the Outline as well as on the first Frame of the Player. However, in UPK 3.5, the Introduction text is never displayed in the Outline and the Show introduction text determines whether the Introduction text appears in the Player at all (effectively, it controls whether the Start Frame is included in the Player or not. Version Difference The Content Development manual for OnDemand 9.1.5 describes the Show introduction text option as working the way described for UPK 3.5. It doesn't work that way; it works the way described for OnDemand 9.1, above. This is clearly a rare case of the documentation being updated before the software. For our purposes, therefore, we need to make sure that the Show introduction text option is always selected when we publish. This option can be found in the Player category of your Options, as shown in the next screenshot: Action Frames It is possible to add Custom Text to the Topic's Bubbles, either in addition to, or instead of, the Template Text. Using the Template Text has several significant advantages, especially when localizing your content or providing sound. However, the Template Text will only ever be able to describe the mechanics of what the user is doing, it cannot provide business context. You should always try to teach more than just key-strokes and mouse-clicks. Specifically, you should always take the opportunity to add business context yourself, through the liberal use of Custom Text in Action Frames. Consider the following example that uses solely the default template texts: Certainly the trainee can carry out the required action and work their way through the exercise, but are they really learning anything? What is the Ext.Ref field, and what is the significance of the value ZBW002342? Should they always enter this value, or are other values possible? Here, we should help the trainee out and teach them something by providing some more information through the use of Custom Text. A better version is shown in the following screenshot: Now the trainee knows exactly what they are entering in the exercise, and understands the business context so they can perform the action correctly when they are doing their actual job. Note that here, we have retained the Template Text (we did not insert the Template Text as Custom Text) which will aid in the translation (although the custom text will still need to be manually translated). We simply added the first paragraph that you see in the Bubble above as Custom Text, and positioned it before the Template Text (the Show custom text first button () is selected by default; you can deselect this if required, to have the Template Text displayed first, but for our purposes we want the Custom Text first).   UPK will run the Template Text in the next line immediately after the Custom Text, so you need to insert an extra line break at the end of the Custom Text if you want the two texts to appear as separate paragraphs. In this example, note that we have continued the scenario that we described in the Introduction pane through into this exercise, by mentioning the customer's name. Again, it is always useful to use a scenario so that the trainee can better relate the exercise to their actual jobs. Note that the text For this exercise...is ZBW002342 will need to be tagged to appear only in See It! and Try It! modes. UPK will run the Template Text in the next line immediately after the Custom Text, so you need to insert an extra line break at the end of the Custom Text if you want the two texts to appear as separate paragraphs. In this example, note that we have continued the scenario that we described in the Introduction pane through into this exercise, by mentioning the customer's name. Again, it is always useful to use a scenario so that the trainee can better relate the exercise to their actual jobs. Note that the text For this exercise...is ZBW002342 will need to be tagged to appear only in See It! and Try It! modes. Whenever practical, you should try to provide some more information, whether this is business context, or a continuation of the scenario you are using, even if this is on every Frame. If you intend for your simulations being used outside of a classroom environment, then you should consider providing exactly the same level of information as the instructor would provide in a classroom. Think about what you would say to the trainee, what additional information or guidance you would give them if you sat next to them, talking them through the simulation, and then add that information into the Bubbles as Custom Text. Remember: training is the effective transfer of knowledge, and if that knowledge is incomplete, then the trainees have not been adequately trained. The End Frame The End Frame is always displayed as the final Frame in the simulation. There is no End Frame equivalent of the Show introduction text option to avoid having this Frame displayed. This is a good thing, as it means that we can use this Frame to provide some final information to the user. This should be seen as a companion to the Start Frame, and should confirm the information presented in the Start Frame. In the Start Frame above, we told the trainee what they would learn. In the End Frame, we should confirm that they have learned this. (This much is standard training theory.) If you have described a scenario in the Start Frame, and followed this through the Action Frames, then you should make reference to this, as well. Suitable End Frame text for our ongoing exercise on SAP user options could be: Although the scenario information is again fairly spurious in this example, it does at least give you an idea of the kind of information that can usefully be included in the End Frame. Again, this information should be tagged for See It! and Try It! modes only. Note that in this example we have also included a message of You have now completed this exercise. This is a nice courtesy, and confirms to the trainee that they have reached the end of the simulation.
Read more
  • 0
  • 0
  • 3854

article-image-glossary-upk-35
Packt
16 Nov 2009
9 min read
Save for later

Glossary in UPK 3.5

Packt
16 Nov 2009
9 min read
A Glossary in UPK effectively associates Web Pages to key terms. UPK will then turn any instance of that key term within a Topic (or other content object) to which that Glossary is assigned into a hyperlink to the associated Web Page. You could do this manually by creating Web Pages (as explained above) and then creating hyperlinks to these manually, but the advantage of using the Glossary functionality is that UPK will 'automatically' find each occurrence of the glossary term and create the hyperlink. I say 'automatically' in quotation marks, as UPK does not do this completely automatically. You have to tell it to go and create the hyperlinks, but once you tell it to do so, it will dutifully go off and find 'every' occurrence of all of the terms in the glossary and create the hyperlinks to the definition. And I say 'every' in quotation marks, because UPK won't necessarily link every instance of the term. UPK allows you to choose (at the Library level) whether it turns literally every occurrence of the term found in a location into a hyperlink, or only the first occurrence. A location here is effectively a single Bubble or Web Page. One could well ask why UPK doesn't provide the option to create a Glossary link only for the first occurrence of the term in a Topic. The simple answer is that it does not necessarily know which occurrence is the first, because the Topic could include Alternative Paths. So UPK takes an over-cautious approach, and considers each block of information separately. This option is specified in the Options panel (menu option Tools|Options), under the Content Defaults|Glossary section, as shown in the following screenshot: Creating a glossary To create a Glossary in UPK, follow these steps: From the main Library screen, click on the folder within which you want to create the Glossary. You can create the Glossary in any folder, but it makes sense to have a single folder that contains the Glossary file itself, and all of the Web Pages used for the terms. Select menu option File|New|Glossary. The Glossary Editor is opened in a new tab within the UPK Developer window, as shown in the following screenshot: To create glossary entries from within the Glossary, follow these steps: Enter the glossary term in the first free Glossary Term field. Click in the Definition Link field on the same line. An ellipsis (...) is displayed on the rightmost side of the field. Click on the ellipsis. The Edit Definition Link dialog box is displayed. Click on the Create New Web Page field. The Save As dialog box is displayed. Navigate to the directory in which you want to save the Glossary definition. Again, it makes sense to save these in the same folder as the Glossary itself. The Name field will default to the glossary term specified in the Glossary Term field. It is recommended that you keep this default, and use the definition term as the Web Page name. You could use any other name, if you wanted to (UPK does not use this name to locate the definition; it uses its own, internal identifier) but using the same name will allow you to easily locate the term Web Page if you need to. Click on the Save button. A new tab is opened for the Web Page. Enter the Glossary description into this page. The Web Page will use the default font and colors. You can override these defaults, if required. Close the Web Page by clicking on the x on the rightmost side of the open tab list. You are passed back to the Glossary Editor. Enter a suitable ToolTip text for the glossary entry in the Tooltip field. This text will be displayed when the user hovers the mouse over the hyperlinked glossary term. If only whole instances of the glossary term should be turned into hyperlinks (for example, if the term is Order then "Orders" and "Ordering" will not be hyperlinked), then select the Match Whole Word option. Otherwise, make sure that this option is not selected. If only text that matches the case of the term should be turned into hyperlinks (for example, if the term is Order then "order" will not be hyperlinked), then select the Match Case field. Otherwise, make sure that this option is not selected. Repeat Steps 1 to 14 for all additional terms that you want to add to the Glossary. To create a glossary entry that uses an existing Web Page for the glossary term, follow these steps: Enter the glossary term in the first free Glossary Term field. Click in the Definition Link field on the same line. An ellipsis (...) is displayed on the rightmost side of the field. Click on the ellipsis. The Edit Definition Link dialog box is displayed. Click on the Create Link button. The Insert Hyperlink dialog box is displayed. Navigate to, and select, the Web Page that contains the glossary description. Click on OK. The Edit Definition Link dialog box is redisplayed. You can edit the Web Page directly from this dialog box, by clicking on the Edit Web Page icon. Click on OK. You are returned to the Glossary tabbed page. Once you have defined all required Glossary entries, save and close the Glossary, by following the steps shown below: Click the Save button to save your changes to the Glossary. The Save As dialog box is displayed. Navigate to the directory in which you want to save the Glossary. Enter a suitable name for the Glossary in the Name field. Close the Glossary Editor by clicking on the x on the rightmost side of the open tab list. An example of a partially-populated Glossary is shown in the next screenshot: You can see from the Definition Link column above that all of the Glossary definition files are stored in the same, single folder, called Glossary. This is the same folder that the Glossary object itself is stored in. Personally, I find it useful to keep all of the content objects for the Glossary in the same single folder. This is not strictly necessary, but it does keep things organized. You will also note that the Tooltip is the same in every case. I tend to always use the tooltip Glossary so that the user knows that the hyperlink links to the Glossary, and not to another form of Web Page. Assigning a Glossary to content objects Creating a Glossary is only half the story. You need to manually assign the Glossary to each object that you want to use that Glossary (that is, for which you want the terms specified in the Glossary to be hyperlinked to the Glossary definitions). Version Difference In OnDemand Version 8.7 and earlier, a single Glossary was created for a Title, and was automatically applied to all content objects within that Title. In UPK 3.5, it is possible to have multiple Glossaries within a single Library, so it is necessary to specify which content objects should use which Glossary. This assignment is done via the content object's Properties, as shown in the screenshot below. The Glossary property is available for Modules, Sections, Topics, and Web Pages. This means that you could potentially assign one Glossary to a Module, another Glossary to a Section within this Module, and a third Glossary to a Topic within that Section. Not that you're very likely to want to do this. But you could. The further implication of the requirement to assign a Glossary to each content object individually is that you can define multiple Glossaries, and assign different Glossaries to different objects. I'd question the wisdom of assigning one glossary to a Module, and then an entirely different Glossary to a Topic within that Module, but I can certainly see the benefit of having multiple Glossaries available in a Library that contained (for example) simulations for multiple applications; you could create application-specific Glossaries (as you no doubt do at the moment) and then assign each Glossary to only the content objects for the relevant application. The downside (for those of us who favor modularization and reuse) is that it is only possible to assign a single Glossary to any given object. So it is not possible to, say, create a company-wide Glossary and separate application-specific Glossaries, and then assign the company-wide Glossary and the relevant application-specific glossary to a single Topic. But more resourceful readers will have already worked out how to get around this limitation. Need a clue? Glossary entries are just Web Pages, and any given Web Page can be reused in multiple places. Need more help? A Web Page can be included in more than one Glossary. So you should first define Web Pages for all of your Glossary terms. You can then create multiple Glossaries (for example, one for each application), and include whichever terms' Web Pages you need to in each of the glossaries. If a term applies to two applications, then simply include the Web Page for that term in the Glossaries for both applications. Simple! Of course there is some slight duplication of effort as you need to create the entry in the actual Glossary content object twice (once in each glossary), but you are reusing the individual definitions, so it could be worse. Unfortunately, unlike Templates, it is not possible to specify a default Glossary in your user defaults. This means that this assignment must be done separately for each content object. However, there are a couple of shortcuts that UPK provides which avoid the need to assign the Glossary to content objects one by one. First, if you select multiple content objects (Modules, Sections, Topics, or Web Pages) and display the Properties pane, then you can assign the Glossary to all of the selected objects in one fell swoop. Second, if you assign a Glossary to a Module, then any new Sections or Titles that you create within that Module – that is, from within the Outline Editor – will inherit this Glossary assignment. However, it is important to note that this will only apply to new content objects. If you assign a Glossary to an outline element and then insert pre-existing content objects into this outline element, then these pre-existing content objects will not inherit the Glossary assignment. Regenerating the Glossary links Glossary links are not created (or updated) automatically. You need to tell UPK to go and search through your content objects and turn any instances of the Glossary terms into links to the Glossary definitions. This is good in that you can at least have control over when it does this, but bad in that it is easy to forget to do so.
Read more
  • 0
  • 0
  • 1713
article-image-session-and-user-joomla-15-part-1
Packt
16 Nov 2009
9 min read
Save for later

The Session and the User with Joomla! 1.5: Part 1

Packt
16 Nov 2009
9 min read
Introduction When a user starts browsing a Joomla! web site, a PHP session is created. Hidden away in the session is user information, this information will either represent a known registered user or a guest. We can interact with the session using the session handler, a JSession object. When we work with the session in Joomla!, we must not use the global PHP $_SESSION variable or any of the PHP session functions. Getting the session handler To interact with the session we use the session handler; this is a JSession object that is globally available via the static JFactory interface. It is imperative that we only use the global JSession object to interact with the PHP session. Directly using $_SESSION or any of the PHP session functions could have unintended consequences. How to do it... To retrieve the JSession object we use JFactory. As JFactory returns an object, we must use =& when assigning the object to a variable. If we do not and our server is running a PHP version prior to PHP 5, we will inadvertently create a copy of the global JSession object. $session =& JFactory::getSession(); How it works... If we look at the JSession class, we will notice that there is a getInstance() method. It is tempting to think of this as synonymous with the JFactory::getSession() method. There is, however, an important difference, the JSession::getInstance() method requires configuration parameters. The JFactory::getSession() method accepts configuration parameters, but they are not required. The first time the JFactory::getSession() method is executed, it is done by the JApplication object (often referred to as mainframe). This creates the session handler. It is the application and JFactory that deal with the configuration of the session. Subsequent usage of the JFactory::getSession() method will not require the creation of the object, and thus simply returns the existing object. The following sequence diagram shows how this process works the first time it is executed by the JApplication object: When the JFactory::getSession() method is subsequently executed, because session will already exist, the _createSession() method is not executed. The diagram is a simplification of the process; additional complexity has not been included because it is outside the scope of this recipe. See also For information about setting and retrieving values in the session, refer to the next two recipes, Adding data to the session and Getting session data. Adding data to the session Data that is set in the session is maintained between client requests. For example, we could display announcements at the top of all our pages and include an option to hide the announcements. Once a user opts to hide the announcements, by setting a value in the session, we would be able to 'remember' this throughout the user's visit. To put this into context, we would set a session value hideAnnouncements to true when a user opts to hide announcements. In subsequent requests, we will be able to retrieve the value of hideAnnouncements from the session and its state will remain the same. In Joomla!, session data is maintained using a JSession object, which we can retrieve using JFactory. This recipe explains how to set data in the session using this object instead of using the global PHP $_SESSION variable. Getting ready We must get the session handler, a JSession object. For more information, refer to the first recipe in this article, Getting the session handler. $session =& JFactory::getSession(); How to do it... The JSession::set() method is used to set a value in the session. The first parameter is the name of the value we want to set; the second is the value itself. $session->set('hideAnnouncements', $value); The JSession::set() method returns the previous value. If no value was previously set, the return value will be null. // set the new value and retrieve the old$oldValue = $session->set('hideAnnouncements', $value);echo 'Hide Announcement was ' . ($oldValue ? 'true' : 'false');echo 'Hide Announcement is now ' . ($value ? 'true' : 'false'); Lastly, we can remove data from the session by setting the value to null. // remove something$session->set('hideAnnouncements', null); How it works... The session contains a namespace-style data structure. Namespaces are required by JSession and by default all values are set in the default namespace. To set a value in a different namespace, we use the optional JSession::set() third parameter. $session->set('something', $value, 'mynamespace'); Sessions aren't just restricted to storing basic values such as strings and integers. The JUser object is a case in point—every session includes an instance of JUser that represents the user the session belongs to. If we add objects to the session, we must be careful. All session data is serialized. To successfully unserialize an object, the class must already be known when the session is restored. For example, the JObject class is safe to serialize because it is loaded prior to restoring the session. $value = new JObject();$session->set('aJObject', $value); If we attempt to do this with a class that is not loaded when the session is restored, we will end up with an object of type __PHP_Incomplete_Class. To overcome this, we can serialize the object ourselves. // serialize the object in the session$session->set('anObject', serialize($anObject)); To retrieve this, we must unserialize the object after we have loaded the class. If we do not do this, we will end up with a string that looks something like this O:7:"MyClass":1:{s:1:"x";s:10:"some value";}. // load the classinclude_once(JPATH_COMPONENT . DS . 'myclass.php');// unserialize the object from the session$value = unserialize($session->get('anObject')); There's more... There is an alternative way of setting data in the session. User state data is also part of the session, but this data allows us to save session data using more complex hierarchical namespaces, for example com_myextension.foo.bar.baz. To access this session data, we use the application object instead of the session handler. // get the application$app =& JFactory::getApplication();// set some data$app->setUserState('com_myextsion.foo.bar.baz, $value); An advantage of using user state data is that we can combine this with request data. For more information refer to the next recipe, Getting session data. The JApplication::setUserState() method is documented as returning the old value. However, a bug prevents this from working; instead the new value is returned. See also For information about retrieving values from the session, refer to the next recipe, Getting session data. Getting session data Data that is set in the session is maintained between client requests. For example if during one request we set the session value of hideAnnouncements to true, as described in the previous recipe, in subsequent requests we will be able to retrieve the value of hideAnnouncements and its state will remain the same. In Joomla!, session data is maintained using the global JSession object. This recipe explains how to get data from the session using this object instead of from the normal global PHP $_SESSION variable. Getting ready We must get the session handler, a JSession object. For more information, refer to the first recipe in this article, Getting the session handler. $session =& JFactory::getSession(); How to do it... We use the JSession::get() method to retrieve data from the session. $value = $session->get('hideAnnouncements'); If the value we attempt to retrieve is not set in the session, the value null is returned. It is possible to specify a default value, which will be returned in instances where the value is not currently set in the session. $defaultValue = false;$value = $session->get('hideAnnouncements', $defaultValue); How it works... The session contains a namespace-style data structure. Namespaces are required by JSession and by default all values are retrieved from the default namespace. To get a value from a different namespace, we use the optional third JSession::get() parameter. $value = $session->get('hideAnnouncements', $defaultValue, 'mynamespace'); It is possible to store objects in the session. However, these require special attention when we extract them from the session. For more information about storing objects in the session, refer to the previous recipe, Adding data to the session. There's more... There is an alternative way of getting data from the session. User state data is also part of the session. The user state data allows us to store session data using more complex hierarchical namespaces, for example com_myextension.foo.bar.baz. To access user state data, we use the application object instead of the session handler. // get the application (this is the same as $mainframe)$app =& JFactory::getApplication();// get some user state data$value = $app->getUserState('com_myextsion.foo.bar.baz'); User state data is usually combined with request data. For example, if we know the request may include a value that we want to use to update the user state data, we use the JApplication::getUserStateFromRequest() method. // get some user state data and update from request$value = $app->getUserStateFromRequest( 'com_myextsion.foo.bar.baz', 'inputName', $defaultValue, 'INTEGER'); The four parameters we provide this method with are the path to the value in the state data, the name of the request input from which we want to update the value, the default value (which is used if there is no value in the request), and the type of value. This method is used extensively for dealing with display state data, such as pagination. // get global default pagination limit$defaultListLimit = $app->getCfg('list_limit');// get limit based on user state data / request data$limit = $app->getUserStateFromRequest( 'global.list.limit', 'limit', $defaultListLimit, 'INTEGER'); See also For information about setting values in the session, refer to the previous recipe, Adding data to the session.
Read more
  • 0
  • 0
  • 3337

article-image-session-and-user-joomla-15-part-2
Packt
16 Nov 2009
8 min read
Save for later

The Session and the User with Joomla! 1.5: Part 2

Packt
16 Nov 2009
8 min read
Getting the user's group ID and type The following organized list describes the user groups in Joomla! 1.5 in a way in which we are all probably familiar. Each of these groups has an ID and a name; these are the group's ID and type respectively. This recipe explains how to find the group ID and group type of the current user. Note that the hard coded group IDs should not generally be used for access control; for this it is best to take advantage of the JAuthorization class. For more information, refer to the Joomla! API site: http://api.joomla.org/. Dynamic permissions There is no administrative functionality that allows the modification of groups. It is, however, possible to programmatically modify groups and group permissions. Most of the time a Joomla! 1.5 extension will opt to implement its own permission management rather than use the incomplete Joomla! solution. Joomla! 1.6, in development at the time of writing, promises a complete GACL (Group Access Control List) implementation. Getting ready To complete this recipe, we need the JUser object that represents the current user. For more information, refer to the Getting the user recipe covered earlier in this article. $user =& JFactory::getUser(); How to do it... The group ID is held in the gid field in the #__users table. We use the JUser::get() method to extract the user's group ID. // determine group ID$groupID = $user->get('gid'); The group type is held in the usertype field in the #__users table. Again, we use the JUser::get() method to extract the user's group ID. $usertype = $user->get('usertype'); How it works... Apart from the obvious format of the data, there is a subtle difference between the gid and usertype fields. The gid field is always populated, where as the usertype field is populated only if the user is not blocked. When dealing with the current user, the user will never be blocked, but when dealing with other users there is a possibility that the usertype field will not be populated. Therefore, we need to be careful that we select the most appropriate field, depending on the context of what we are doing. The user groups that we have mentioned are maintained in the #__core_acl_aro_groups table, or as it can also be called, the Access Control List Access Request Object Groups table. This table holds a tree structure, as indicated in the introduction of the recipe. What is noticeable is that Public Frontend and Public Backend are both technically user groups. As this is a tree structure, we can use the left and right values to perform all sorts of neat tricks. The #__core_acl_aro_groups table employs the nested set model: http://dev.mysql.com/tech-resources/articles/hierarchical-data.html. See also The next recipe explains how to use the Public, Registered, and Special access levels. Restricting a user's access using Public, Registered, and Special In Joomla! we often define access based on three simple access levels. These levels relate to the user groups described in the previous recipe, Getting the user's group ID and type. This recipe explains how to use these access levels to restrict access to resources. Public (0) Registered (1) Special (2) Getting ready To complete this recipe, we need the JUser object that represents the current user. For more information, refer to the Getting the user recipe covered earlier in this article. $user =& JFactory::getUser(); How to do it... The JUser::get() method can be used to retrieve the user's aid. This is the access level number, as shown in braces in the recipe introduction. // get access level$aid = $user->get('aid'); For each resource we want to restrict access to, we must define the required access level. This is normally done in a database TINYINT(3) field named access. This can then be used to restrict the access using a database query (used when listing)... // get the DBO$db =& JFactory::getDBO();// prepare access field name$access = $db->nameQuote('access');// restrict query by access level$where = "WHERE $access <= " . intval($aid); ...or when viewing a single record. Note that ALERTNOTAUTH is a core translation string, which in en-GB is equivalent to You are not authorised to view this resource. // make sure the user has the necessary access rightsif ($table->get('access') > $aid) { JError::raiseError(403, JText::_('ALERTNOTAUTH')); jexit();} How it works... A good example of this in action is the content component. If we take a look at the article manager, we can see that there is an Access Level associated with each article that determines which users can access the article. These access levels are relatively primitive and we don't have any bona fide control over them. So how do they translate into concrete user groups? The following table describes the seven user groups and the guest group, and shows how these relate to the access levels: User Group User Group ID Access Level Access Level ID None (Guest) 0 Public 0 Registered 18 Registered 1 Author 19 Special 2 Editor 20 Special 2 Publisher 21 Special 2 Manager 23 Special 2 Administrator 24 Special 2 Super Administrator 25 Special 2 See also The previous recipe explains how to work with the user group ID and group type. This can also be useful for dealing with access control. Getting the user's parameters User parameters provide a mechanism for including additional user data without the need to modify the database. The core user parameters are defined in the XML files located in the administratorcomponentscom_usersmodels folder. Getting ready To complete this recipe, we need the JUser object that represents the current user. For more information, refer to the Getting the user recipe. $user =& JFactory::getUser(); How to do it... To retrieve a parameter we must first know the name of the parameter we want to retrieve. One example of a common parameter is timezone. This is an integer that defines the hours offset from UTC (Coordinated Universal Time), also known as GMT (Greenwich Mean Time) and Z (Zulu). To retrieve a parameter, we can use the JUser::getParam() method. $timezone = $user->getParam('timezone'); It is also possible to provide a default value. This is useful especially if the user we are dealing with is a guest because guest users do not have any parameters defined by default. $timezone = $user->getParam('timezone', '0'); How it works... A user's parameters are represented as a JParameter object. We do not have to interact directly with this object because JUser will do this for us. A minor problem with this method is the default value. Technically the XML files define the default values, but by default these XML files are not loaded by the JUser class. Therefore, the XML defined default values are not employed. One way to overcome this is to interact directly with the JParameter object. The next section explains how to do this. There's more... The JUser::getParamters() method allows us to directly access the user's JParameter object. Note that we must use =& when assigning the return value to a variable. If we do not and we are using a PHP version prior to PHP 5, we will inadvertently create a copy of the returned JParameter object. // get parameters with XML definition file loaded$params =& $user->getParameters(true); When we retrieve the JParameter object there are two optional parameters. The first parameter $loadsetupfile determines whether or not the XML file that defines the user's parameters should be loaded. Loading this file gives meaning to the data and also provides default values for defined data. For information about the second optional parameter, refer to the recipe, Extending and editing user parameters. To retrieve a value, we use the JParameter::get() method. We pass two parameters to this method—the name of the parameter we want the value of, and the default value to return if the parameter does not exist. // get timezone (hours UTC offset)$timezone = $params->get('timezone', '0'); See also The next recipe, Setting the user's parameters, explains how to set a value in the user's parameters. Setting the user's parameters User parameters provide a mechanism for including additional user data without the need to modify the database. The parameters are defined in the XML files located in the administratorcomponentscom_usersmodels folder. User parameters are not restricted to the parameters defined in the XML files. It is perfectly acceptable to add additional parameters. Getting ready To complete this recipe, we need the JUser object that represents the current user. For more information, refer to the Getting the user recipe. $user =& JFactory::getUser(); How to do it... To set the value of a parameter, we use the JUser::setParam() method. This method requires two parameters—the name of the parameter we want to set and the value to which we want to set the parameter. For example, we could change the user's editor preference to use no editor. // set value of someparameter to some value$user->setParam('editor', 'none'); There's more... If we have retrieved the JParamter object directly from the user, we can alternatively use that object to set the user's parameters. For information about retrieving the user's parameters, refer to the previous recipe, Getting the user's parameters. To set data in the JParameter object, we use the JParameter::set() method. // set value of someparameter to some value$params->set('editor', 'none'); See also The previous recipe explains how to get a value from the user's parameters.
Read more
  • 0
  • 0
  • 1604
Modal Close icon
Modal Close icon