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-concrete5-mastering-auto-nav-advanced-navigation
Packt
12 Apr 2011
9 min read
Save for later

concrete5: Mastering Auto-Nav for Advanced Navigation

Packt
12 Apr 2011
9 min read
concrete5 Beginner's Guide Create and customize your own website with the Concrete5 Beginner's Guide         Autonav introduction Before we start customizing the autonav block, we're going to have a quick look at the different options and the output. It's very helpful to be familiar with all the block options as well as knowing the HTML output the block generates before you start extending the block. Preparation You may have the autonav block included in your theme, more precisely in header.php of your theme. Since we're going to play with navigation, we should undo this modification; changing the options in header.php would be a bit annoying otherwise. If you're done with this article, you might want to put the code back in place; it's mostly to make it easier to work with the custom templates we're going to build. Time for action – undoing autonav block integration Open header.php from your theme; it's located in the themes/c5book/elements directory (Code Download-ch:7). Since the following code snippet doesn't show the complete file, make sure you replace the correct lines. Everything is underneath the HTML tag with the ID header: <div id="wrapper"> <div id="page"> <div id="header_line_top"></div> <div id="header"> <?php $a = new Area('Header Nav'); $a->display($c); ?> </div> <div id="header_line_bottom"></div> Save header.php and go back to your page. Make sure the navigation is still there, if it isn't go back to edit the page and add a new autonav block in Header Nav. After you've added it, change the custom template to Header Menu. What just happened? We had to undo a modification done before this article. The code which printed the autonav block directly from the template would be fine if your navigation didn't change. However, since we're working on the autonav block for a whole article, we had to remove this code and replace it with the default code for an editable area. Autonav options The autonav block comes with a bunch of options you can use to create the correct hierarchical output of pages in your navigation. While you probably have to play around with it for a bit to get used to all the options, we're still going to look at a few possible configurations which we'll need later in this article. Autonav page structure The example configurations we're going to look at use the structure shown in the following screenshot. We won't need to tick the checkbox for system pages, which would show the dashboard and some built-in pages. We don't want to include them in our navigation anyway. It doesn't matter if your structure looks different at this point; the examples are easy to understand even if your result looks a bit different. Page order By default, the autonav block uses the sort order which you can see in the sitemap as well. This usually makes sense because it offers the biggest flexibility. Remember, you can arrange pages by dragging their icon to the place where you want the page to be. In all our examples you can choose whatever order you like; it doesn't have an effect on our templates. Example 1 - showing all pages The most basic configuration shows all pages, no matter where we are and no matter how many pages there are. This configuration is useful when you create a JavaScript-based navigation which displays subpages dynamically without reloading the page. The settings should be obvious and the result as well; it will show all pages shown in the preceding structure. When you create JavaScript drop-down navigation, you have to generate HTML code for all elements you want to show, but that doesn't necessarily mean that you want to print all elements. Assuming you've got hundreds of pages, would you like to see all of them in the drop-down menu? Probably not, and this is why you can manually specify the number of page levels you'd like to print. Use this for the drop-down navigation we're going to create later in this article. Example 2 – showing relevant subpages In the structure just shown, assume you're on About, which has two direct child pages. If we wanted to display the two subpages in the left sidebar of our page, we could use the following settings: Example 3 – showing relevant subpages starting from the top For a site where you only have a single navigation, probably on the left-hand side, you have to start at the top and include all the relevant subpages. The settings are similar, but this time we start at the top and include the level below the current subpage as well by using these settings: If you're on the About page again, you'd see all pages on the top, along with the About page and the two subpages of it. Autonav output The autonav controller produces an HTML output which is compatible with most jQuery libraries you can find. It uses an UL/LI structure to create a proper hierarchical representation of the pages we show in our navigation. Before we look at the actual output, here are some words about the process which generates the output. The autonav block controller uses all the settings you make when you add the block. It then creates an array of pages which doesn't have any children—it's a flat array. Unlike what some of you would expect, there's no real recursion in the structure which you have to process in the block template. How's an autonav block template supposed to print a hierarchical structure? That's not too difficult; there's a property called level for each element in the array. You simply have to check what happens to that level. Is the level of the current page element bigger than the one from the previous element? If yes, create a new child to the current page. Does it decrease? If yes, close the HTML tags for the child elements you created when the level increased. Does this sound a bit abstract? Let's look at a simplified, not working, but commented autonav template: <?php defined('C5_EXECUTE') or die(_("Access Denied.")); // get the list of all pages matching the selection $aBlocks = $controller->generateNav(); $nh = Loader::helper('navigation'); echo("<ul class="nav">"); // loop through all the pages foreach($aBlocks as $ni) { $_c = $ni->getCollectionObject(); // get the level of the current element. // This is necessary to create the proper indentation. $thisLevel = $ni->getLevel(); // the current page has a higher level than the previous // page which means that we have to print another UL // element to indent the next pages if ($thisLevel > $lastLevel) { echo("<ul>"); } // the current page has a lower level compared to // the previous page. We have to close all the open // LI and UL elements we've previously opened else if ($thisLevel < $lastLevel) { for ($j = $thisLevel; $j < $lastLevel; $j++) { if ($lastLevel - $j > 1) { echo("</li></ul>"); } else { echo("</li></ul></li>"); } } } // when adding a page, see "echo('<li>..." below // the tag isn't closed as nested UL elements // have to be within the LI element. We always close // them in an iteration later else if ($i > 0) { echo("</li>"); } // output the page information, name and link echo('<li><a href="' . $ni->getURL() . '">' . $ni->getName() . '</a>'); // We have to compare the current page level // to the level of the previous page, safe // it in $lastLevel $lastLevel = $thisLevel; $i++; } // When the last page has been printed, it // can happen that we're not in the top level // and therefore have to close all the child // level we haven't closed yet $thisLevel = 0; for ($i = $thisLevel; $i <= $lastLevel; $i++) { echo("</li></ul>"); } ?> The templates we're going to create don't change a lot from the default PHP template. We mostly use the HTML structure the default template generates and only add some CSS and JavaScript. Understanding every detail of the default autonav template isn't necessary, but still helps you to get the most out of the autonav block. What we must understand is the HTML structure shown as follows—it's what you'll have to work with when you create a custom navigation or layout: <ul class="nav"> <li class="nav-path-selected"> <a class="nav-path-selected" href="/">Home</a> </li> <li class="nav-selected nav-path-selected"> <a class="nav-selected nav-path-selected" href="/index.php/about/">About</a> <ul> <li> <a href="/index.php/about/press-room/">Press Room</a> </li> <li> <a href="/index.php/about/guestbook/">Guestbook</a> </li> </ul> </li> <li> <a href="/index.php/search/">Search</a> </li> <li> <a href="/index.php/news/">News</a> </li> </ul> Since you're supposed to have some HTML knowledge to read this book, it should be fairly easy to understand the preceding structure. Each new level added a new ul element which contains an li element for each page along with an a element to make it clickable. Child pages within a ul element belong to their parent, meaning that the li element of the parent is closed when all the children have been printed. The output uses the default template which adds some classes you can use to style the navigation: nav: The main ul tag contains this class. Use it to access all elements of the navigation. nav-selected: This class is assigned to the elements if they belong to the current page. nav-path-selected: This class can be found on pages which are above the current page. They belong to the path of the current page, and are thus called path-selected.
Read more
  • 0
  • 0
  • 3676

article-image-restservices-finagle-and-finch
Packt
03 Nov 2015
9 min read
Save for later

RESTServices with Finagle and Finch

Packt
03 Nov 2015
9 min read
In this article by Jos Dirksen, the author of RESTful Web Services with Scala, we'll only be talking about Finch. Note, though, that most of the concepts provided by Finch are based on the underlying Finagle ideas. Finch just provides a very nice REST-based set of functions to make working with Finagle very easy and intuitive. (For more resources related to this topic, see here.) Finagle and Finch are two different frameworks that work closely together. Finagle is an RPC framework, created by Twitter, which you can use to easily create different types of services. On the website (https://github.com/twitter/finagle), the team behind Finagle explains it like this: Finagle is an extensible RPC system for the JVM, used to construct high-concurrency servers. Finagle implements uniform client and server APIs for several protocols, and is designed for high performance and concurrency. Most of Finagle's code is protocol agnostic, simplifying the implementation of new protocols. So, while Finagle provides the plumbing required to create highly scalable services, it doesn't provide direct support for specific protocols. This is where Finch comes in. Finch (https://github.com/finagle/finch) provides an HTTP REST layer on top of Finagle. On their website, you can find a nice quote that summarizes what Finch aims to do: Finch is a thin layer of purely functional basic blocks atop of Finagle for building composable REST APIs. Its mission is to provide the developers simple and robust REST API primitives being as close as possible to the bare metal Finagle API. Your first Finagle and Finch REST service Let's start by building a minimal Finch REST service. The first thing we need to do is to make sure we have the correct dependencies. To use Finch, all you have to do is to add the following dependency to your SBT file: "com.github.finagle" %% "finch-core" % "0.7.0" With this dependency added, we can start coding our very first Finch service. The next code fragment shows a minimal Finch service, which just responds with a Hello, Finch! message: package org.restwithscala.chapter2.gettingstarted import io.finch.route._ import com.twitter.finagle.Httpx object HelloFinch extends App { Httpx.serve(":8080", (Get / "hello" />"Hello, Finch!").toService) println("Press <enter> to exit.") Console.in.read.toChar } When this service receives a GET request on the URL path hello, it will respond with a Hello, Finch! message. Finch does this by creating a service (using the toService function) from a route (more on what a route is will be explained in the next section) and using the Httpx.serve function to host the created service. When you run this example, you'll see an output as follows: [info] Loading project definition from /Users/jos/dev/git/rest-with-scala/project [info] Set current project to rest-with-scala (in build file:/Users/jos/dev/git/rest-with-scala/) [info] Running org.restwithscala.chapter2.gettingstarted.HelloFinch Jun 26, 2015 9:38:00 AM com.twitter.finagle.Init$$anonfun$1 apply$mcV$sp INFO: Finagle version 6.25.0 (rev=78909170b7cc97044481274e297805d770465110) built at 20150423-135046 Press <enter> to exit. At this point, we have an HTTP server running on port 8080. When we make a call to http://localhost:8080/hello, this server will respond with the Hello, Finch! message. To test this service, you can make a HTTP requests in Postman like this: If you don't want to use a GUI to make the requests, you can also use the following Curl command: curl'http://localhost:8080/hello' HTTP verb and URL matching An important part of every REST framework is the ability to easily match HTTP verbs and the various path segments of the URL. In this section, we'll look at the tools Finch provides us with. Let's look at the code required to do this (the full source code for this example can be found at https://github.com/josdirksen/rest-with-scala/blob/master/chapter-02/src/main/scala/org/restwithscala/chapter2/steps/FinchStep1.scala): package org.restwithscala.chapter2.steps import com.twitter.finagle.Httpx import io.finch.request._ import io.finch.route._ import io.finch.{Endpoint => _} object FinchStep1 extends App { // handle a single post using a RequestReader valtaskCreateAPI = Post / "tasks" /> ( for { bodyContent<- body } yield s"created task with: $bodyContent") // Use matchers and extractors to determine which route to call // For more examples see the source file. valtaskAPI = Get / "tasks" /> "Get a list of all the tasks" | Get / "tasks" / long /> ( id =>s"Get a single task with id: $id" ) | Put / "tasks" / long /> ( id =>s"Update an existing task with id $id to " ) | Delete / "tasks" / long /> ( id =>s"Delete an existing task with $id" ) // simple server that combines the two routes and creates a val server = Httpx.serve(":8080", (taskAPI :+: taskCreateAPI).toService ) println("Press <enter> to exit.") Console.in.read.toChar server.close() } In this code fragment, we created a number of Router instances that process the requests, which we sent from Postman. Let's start by looking at one of the routes of the taskAPI router: Get / "tasks" / long /> (id =>s"Get a single task with id: $id"). The following table explains the various parts of the route: Part Description Get While writing routers, usually the first thing you do is determine which HTTP verb you want to match. In this case, this route will only match the GET verb. Besides the Get matcher, Finch also provides the following matchers: Post, Patch, Delete, Head, Options, Put, Connect, and Trace. "tasks" The next part of the route is a matcher that matches a URL path segment. In this case, we match the following URL: http://localhost:8080/tasks. Finch will use an implicit conversion to convert this String object to a finch Matcher object. Finch also has two wildcard Matchers: * and **. The * matcher allows any value for a single path segment, and the ** matcher allows any value for multiple path segments. long The next part in the route is called an Extractor. With an extractor, you turn part of the URL into a value, which you can use to create the response (for example, retrieve an object from the database using the extracted ID). The long extractor, as the name implies, converts the matching path segment to a long value. Finch also provides an int, string, and Boolean extractor. long =>B The last part of the route is used to create the response message. Finch provides different ways of creating the response, which we'll show in the other parts of this article. In this case, we need to provide Finch with a function that transforms the long value we extracted, and return a value Finch can convert to a response (more on this later). In this example, we just return a String.  If you've looked closely at the source code, you would have probably noticed that Finch uses custom operators to combine the various parts of a route. Let's look a bit closer at those. With Finch, we get the following operators (also called combinators in Finch terms): / or andThen: With this combinatory, you sequentially combine various matchers and extractors together. Whenever the first part matches, the next one is called. For instance: Get / "path" / long. | or orElse: This combinator allows you to combine two routers (or parts thereof) together as long as they are of the same type. So, we could do (Get | Post) to create a matcher, which matches the GET and POST HTTP verbs. In the code sample, we've also used this to combine all the routes that returned a simple String into the taskAPI router. /> or map: With this combinatory, we pass the request and any extracted values from the path to a function for further processing. The result of the function that is called is returned as the HTTP response. As you'll see in the rest of the article, there are different ways of processing the HTTP request and creating a response. :+:: The final combinator allows you to combine two routers together of different types. In the example, we have two routers. A taskAPI, that returns a simple String, and a taskCreateAPI, which uses a RequestReader (through the body function) to create the response. We can't combine these with | since the result is created using two different approaches, so we use the :+:combinator. We just return simple Strings whenever we get a request. In the next section, we'll look at how you can use RequestReader to convert the incoming HTTP requests to case classes and use those to create a HTTP response. When you run this service, you'll see an output as follows: [info] Loading project definition from /Users/jos/dev/git/rest-with-scala/project [info] Set current project to rest-with-scala (in build file:/Users/jos/dev/git/rest-with-scala/) [info] Running org.restwithscala.chapter2.steps.FinchStep1 Jun 26, 2015 10:19:11 AM com.twitter.finagle.Init$$anonfun$1 apply$mcV$sp INFO: Finagle version 6.25.0 (rev=78909170b7cc97044481274e297805d770465110) built at 20150423-135046 Press <enter> to exit. Once the server is started, you can once again use Postman(or any other REST client) to make requests to this service (example requests can be found at https://github.com/josdirksen/rest-with-scala/tree/master/common): And once again, you don't have to use a GUI to make the requests. You can test the service with Curl as follows: # Create task curl 'http://localhost:8080/tasks' -H 'Content-Type: text/plain;charset=UTF-8' --data-binary $'{ntaskdatan}' # Update task curl 'http://localhost:8080/tasks/1' -X PUT -H 'Content-Type: text/plain;charset=UTF-8' --data-binary $'{ntaskdatan}' # Get all tasks curl'http://localhost:8080/tasks' # Get single task curl'http://localhost:8080/tasks/1' Summary This article only showed a couple of the features Finch provides. But it should give you a good head start toward working with Finch. Resources for Article: Further resources on this subject: RESTful Java Web Services Design [article] Creating a RESTful API [article] Scalability, Limitations, and Effects [article]
Read more
  • 0
  • 0
  • 3675

Packt
08 Sep 2015
17 min read
Save for later

The Symfony Framework – Installation and Configuration

Packt
08 Sep 2015
17 min read
 In this article by Wojciech Bancer, author of the book, Symfony2 Essentials, we will learn the basics of Symfony, its installation, configuration, and use. The Symfony framework is currently one of the most popular PHP frameworks existing within the PHP developer's environment. Version 2, which was released a few years ago, has been a great improvement, and in my opinion was one of the key elements for making the PHP ecosystem suitable for larger enterprise projects. The framework version 2.0 not only required the modern PHP version (minimal version required for Symfony is PHP 5.3.8), but also uses state-of-the-art technology — namespaces and anonymous functions. Authors also put a lot of efforts to provide long term support and to minimize changes, which break the compatibility between versions. Also, Symfony forced developers to use a few useful design concepts. The key one, introduced in Symfony, was DependencyInjection. (For more resources related to this topic, see here.) In most cases, the article will refer to the framework as Symfony2. If you want to look over the Internet or Google about this framework, apart from using Symfony keyword you may also try to use the Symfony2 keyword. This was the way recommended some time ago by one of the creators to make searching or referencing to the specific framework version easier in future. Key reasons to choose Symfony2 Symfony2 is recognized in the PHP ecosystem as a very well-written and well-maintained framework. Design patterns that are recommended and forced within the framework allow work to be more efficient in the group, this allows better tests and the creation of reusable code. Symfony's knowledge can also be verified through a certificate system, and this allows its developers to be easily found and be more recognized on the market. Last but not least, the Symfony2 components are used as parts of other projects, for example, look at the following: Drupal phpBB Laravel eZ Publish and more Over time, there is a good chance that you will find the parts of the Symfony2 components within other open source solutions. Bundles and extendable architecture are also some of the key Symfony2 features. They not only allow you to make your work easier through the easy development of reusable code, but also allows you to find smaller or larger pieces of code that you can embed and use within your project to speed up and make your work faster. The standards of Symfony2 also make it easier to catch errors and to write high-quality code; its community is growing every year. The history of Symfony There are many Symfony versions around, and it's good to know the differences between them to learn how the framework was evolving during these years. The first stable Symfony version — 1.0 — was released in the beginning of 2007 and was supported for three years. In mid-2008, version 1.1 was presented, which wasn't compatible with the previous release, and it was difficult to upgrade any old project to this. Symfony 1.2 version was released shortly after this, at the end of 2008. Migrating between these versions was much easier, and there were no dramatic changes in the structure. The final versions of Symfony 1's legacy family was released nearly one year later. Simultaneously, there were two version releases, 1.3 and 1.4. Both were identical, but Symfony 1.4 did not have deprecated features, and it was recommended to start new projects with it. Version 1.4 had 3 years of support. If you look into the code, version 1.x was very different from version 2. The company that was behind Symfony (the French company, SensioLabs) made a bold move and decided to rewrite the whole framework from scratch. The first release of Symfony2 wasn't perfect, but it was very promising. It relied on Git submodules (the composer did not exist back then). The 2.1 and 2.2 versions were closer to the one we use now, although it required a lot of effort to migrate to the upper level. Finally, the Symfony 2.3 was released — the first long-term support version within the 2.x branch. After this version, the changes provided within the next major versions (2.4, 2.5, and 2.6) are not so drastic and usually they do not break compatibility. This article was written based on the latest stable Symfony 2.7.4 version and was tested with PHP 5.5). This Symfony version is marked as the so called long-term support version, and updates for it will be released for 3 years since the first 2.7 version release. Installation Prior to installing Symfony2, you don't need to have a configured web server. If you have at least PHP version 5.4, you can use the standalone server provided by Symfony2. This server is suitable for development purposes and should not be used for production. It is strongly recommend to work with a Linux/UNIX system for both development and production deployment of Symfony2 framework applications. While it is possible to install and operate on a Windows box, due to its different nature, working with Windows can sometimes force you to maintain a separate fragment of code for this system. Even if your primary OS is Windows, it is strongly recommended to configure Linux system in a virtual environment. Also, there are solutions that will help you in automating the whole process. As an example, see more on https://www.vagrantup.com/ website. To install Symfony2, you can use a few methods as follows: Use a new Symfony2 installer script (currently, the only officially recommended). Please note that installer requires at least PHP 5.4. Use a composer dependency manager to install a Symfony project. Download a zip or tgz package and unpack it. It does not really matter which method you choose, as they all give you similar results. Installing Symfony2 by using an installer To install Symfony2 through an installer, go to the Symfony website at http://symfony.com/download, and install the Symfony2 installer by issuing the following commands: $ sudo curl -LsS http://symfony.com/installer -o /usr/local/bin/symfony $ sudo chmod +x /usr/local/bin/symfony After this, you can install Symfony by just typing the following command: $ symfony new <new_project_folder> To install the Symfony2 framework for a to-do application, execute the following command: $ symfony new <new_project_folder> This command installs the latest Symfony2 stable version on the newly created todoapp folder, creates the Symfony2 application, and prepares some basic structure for you to work with. After the app creation, you can verify that your local PHP is properly configured for Symfony2 by typing the following command: $ php app/check.php If everything goes fine, the script should complete with the following message: [OK] Your system is ready to run Symfony projects Symfony2 is equipped with a standalone server. It makes development easier. If you want to run this, type the following command: $ php app/console server:run If everything went alright, you will see a message that your server is working on the IP 127.0.0.1 and port 8000. If there is an error, make sure you are not running anything else that is listening on port 8000. It is also possible to run the server on a different port or IP, if you have such a requirement, by adding the address and port as a parameter, that is: $ php app/console server:run 127.0.0.1:8080 If everything works, you can now type the following: http://127.0.0.1:8000/ Now, you will visit Symfony's welcome page. This page presents you with a nice welcome information and useful documentation link. The Symfony2 directory structure Let's dive in to the initial directory structure within the typical Symfony application. Here it is: app bin src vendor web While Symfony2 is very flexible in terms of directory structure, it is recommended to keep the basic structure mentioned earlier. The following table describes their purpose: Directory Used for app This holds information about general configuration, routing, security configuration, database parameters, and many others. It is also the recommended place for putting new view files. This directory is a starting point. bin It holds some helper executables. It is not really important during the development process, and rarely modified. src This directory holds the project PHP code (usually your bundles). vendor These are third-party libraries used within the project. Usually, this directory contains all the open source third-party bundles, libraries, and other resources. It's worth to mention that it's recommended to keep the files within this directory outside the versioning system. It means that you should not modify them under any circumstances. Fortunately, there are ways to modify the code, if it suits your needs more. This will be demonstrated when we implement user management within our to-do application. web This is the directory that is accessible through the web server. It holds the main entry point to the application (usually the app.php and app_dev.php files), CSS files, JavaScript files, and all the files that need to be available through the web server (user uploadable files). So, in most cases, you will be usually modifying and creating the PHP files within the src/ directory, the view and configuration files within the app/ directory, and the JS/CSS files within the web/ directory. The main directory also holds a few files as follows: .gitignore README.md composer.json composer.lock The .gitignore file's purpose is to provide some preconfigured settings for the Git repository, while the composer.json and composer.lock files are the files used by the composer dependency manager. What is a bundle? Within the Symfony2 application, you will be using the "bundle" term quite often. Bundle is something similar to plugins. So it can literally hold any code controllers, views, models, and services. A bundle can integrate other non-Symfony2 libraries and hold some JavaScript/CSS code as well. We can say that almost everything is a bundle in Symfony2; even some of the core framework features together form a bundle. A bundle usually implements a single feature or functionality. The code you are writing when you write a Symfony2 application is also a bundle. There are two types of bundles. The first kind of bundle is the one you write within the application, which is project-specific and not reusable. For this purpose, there is a special bundle called AppBundle created for you when you install the Symfony2 project. Also, there are reusable bundles that are shared across the various projects either written by you, your team, or provided by a third-party vendors. Your own bundles are usually stored within the src/ directory, while the third-party bundles sit within the vendor/ directory. The vendor directory is used to store third-party libraries and is managed by the composer. As such, it should never be modified by you. There are many reusable open source bundles, which help you to implement various features within the application. You can find many of them to help you with User Management, writing RESTful APIs, making better documentation, connecting to Facebook and AWS, and even generating a whole admin panel. There are tons of bundles, and everyday brings new ones. If you want to explore open source bundles, and want to look around what's available, I recommend you to start with the http://knpbundles.com/ website. The bundle name is correlated with the PHP namespace. As such, it needs to follow some technical rules, and it needs to end with the Bundle suffix. A few examples of correct names are AppBundle and AcmeDemoBundle, CompanyBlogBundle or CompanySocialForumBundle, and so on. Composer Symfony2 is built based on components, and it would be very difficult to manage the dependencies between them and the framework without a dependency manager. To make installing and managing these components easier, Symfony2 uses a manager called composer. You can get it from the https://getcomposer.org/ website. The composer makes it easy to install and check all dependencies, download them, and integrate them to your work. If you want to find additional packages that can be installed with the composer, you should visit https://packagist.org/. This site is the main composer repository, and contains information about most of the packages that are installable with the composer. To install the composer, go to https://getcomposer.org/download/ and see the download instruction. The download instruction should be similar to the following: $ curl -sS https://getcomposer.org/installer | php If the download was successful, you should see the composer.phar file in your directory. Move this to the project location in the same place where you have the composer.json and composer.lock files. You can also install it globally, if you prefer to, with these two commands: $ curl -sS https://getcomposer.org/installer | php $ sudo mv composer.phar /usr/local/bin/composer You will usually need to use only three composer commands: require, install, and update. The require command is executed when you need to add a new dependency. The install command is used to install the package. The update command is used when you need to fetch the latest version of your dependencies as specified within the JSON file. The difference between install and update is subtle, but very important. If you are executing the update command, your composer.lock file gets updated with the version of the code you just fetched and downloaded. The install command uses the information stored in the composer.lock file and the fetch version stored in this file. When to use install? For example, if you deploy the code to the server, you should use install rather than update, as it will deploy the version of the code stored in composer.lock, rather than download the latest version (which may be untested by you). Also, if you work in a team and you just got an update through Git, you should use install to fetch the vendor code updated by other developers. You should use the update command if you want to check whether there is an updated version of the package you have installed, that is, whether a new minor version of Symfony2 will be released, then the update command will fetch everything. As an example, let's install one extra package for user management called FOSUserBundle (FOS is a shortcut of Friends of Symfony). We will only install it here; we will not configure it. To install FOSUserBundle, we need to know the correct package name and version. The easiest way is to look in the packagist site at https://packagist.org/ and search for the package there. If you type fosuserbundle, the search should return a package called friendsofsymfony/user-bundle as one of the top results. The download counts visible on the right-hand side might be also helpful in determining how popular the bundle is. If you click on this, you will end up on the page with the detailed information about that bundle, such as homepage, versions, and requirements of the package. Type the following command: $ php composer.phar require friendsofsymfony/user-bundle ^1.3 Using version ^1.3 for friendsofsymfony/user-bundle ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing friendsofsymfony/user-bundle (v1.3.6) Loading from cache friendsofsymfony/user-bundle suggests installing willdurand/propel-typehintable-behavior (Needed when using the propel implementation) Writing lock file Generating autoload files ... Which version of the package you choose is up to you. If you are interested in package versioning standards, see the composer website at https://getcomposer.org/doc/01-basic-usage.md#package-versions to get more information on it. The composer holds all the configurable information about dependencies and where to install them in a special JSON file called composer.json. Let's take a look at this: { "name": "wbancer/todoapp", "license": "proprietary", "type": "project", "autoload": { "psr-0": { "": "src/", "SymfonyStandard": "app/SymfonyStandard/" } }, "require": { "php": ">=5.3.9", "symfony/symfony": "2.7.*", "doctrine/orm": "~2.2,>=2.2.3,<2.5", // [...] "incenteev/composer-parameter-handler": "~2.0", "friendsofsymfony/user-bundle": "^1.3" }, "require-dev": { "sensio/generator-bundle": "~2.3" }, "scripts": { "post-root-package-install": [ "SymfonyStandard\\Composer::hookRootPackageInstall" ], "post-install-cmd": [ // post installation steps ], "post-update-cmd": [ // post update steps ] }, "config": { "bin-dir": "bin" }, "extra": { // [...] } } The most important section is the one with the require key. It holds all the information about the packages we want to use within the project. The key scripts contain a set of instructions to run post-install and post-update. The extra key in this case contains some settings specific to the Symfony2 framework. Note that one of the values in here points out to the parameter.yml file. This file is the main file holding the custom machine-specific parameters. The meaning of the other keys is rather obvious. If you look into the vendor/ directory, you will notice that our package has been installed in the vendor/friendsofsymfony/user-bundle directory. The configuration files Each application has a need to hold some global and machine-specific parameters and configurations. Symfony2 holds configuration within the app/config directory and it is split into a few files as follows: config.yml config_dev.yml config_prod.yml config_test.yml parameters.yml parameters.yml.dist routing.yml routing_dev.yml security.yml services.yml All the files except the parameters.yml* files contain global configuration, while the parameters.yml file holds machine-specific information such as database host, database name, user, password, and SMTP configuration. The default configuration file generated by the new Symfony command will be similar to the following one. This file is auto-generated during the composer install: parameters: database_driver: pdo_mysql database_host: 127.0.0.1 database_port: null database_name: symfony database_user: root database_password: null mailer_transport: smtp mailer_host: 127.0.0.1 mailer_user: null mailer_password: null secret: 93b0eebeffd9e229701f74597e10f8ecf4d94d7f As you can see, it mostly holds the parameters related to database, SMTP, locale settings, and secret key that are used internally by Symfony2. Here, you can add your custom parameters using the same syntax. It is a good practice to keep machine-specific data such as passwords, tokens, api-keys, and access keys within this file only. Putting passwords in the general config.yml file is considered as a security risk bug. The global configuration file (config.yml) is split into a few other files called routing*.yml that contain information about routing on the development and production configuration. The file called as security.yml holds information related to authentication and securing the application access. Note that some files contains information for development, production, or test mode. You can define your mode when you run Symfony through the command-line console and when you run it through the web server. In most cases, while developing you will be using the dev mode. The Symfony2 console To finish, let's take a look at the Symfony console script. We used it before to fire up the development server, but it offers more. Execute the following: $ php app/console You will see a list of supported commands. Each command has a short description. Each of the standard commands come with help, so I will not be describing each of them here, but it is worth to mention a few commonly used ones: Command Description app/console: cache:clear Symfony in production uses a lot of caching. Therefore, if you need to change values within a template (twig) or within configuration files while in production mode, you will need to clear the cache. Cache is also one of the reasons why it's worth to work in the development mode. app/console container:debug Displays all configured public services app/console router:debug Displays all routing configuration along with method, scheme, host, and path. app/console security:check Checks your composer and packages version against known security vulnerabilities. You should run this command regularly. Summary In this article, we have demonstrated how to use the Symfony2 installer, test the configuration, run the deployment server, and play around with the Symfony2 command line. We have also installed the composer and learned how to install a package using it. To demonstrate how Symfony2 enables you to make web applications faster, we will try to learn through examples that can be found in real life. To make this task easier, we will try to produce a real to-do web application with modern look and a few working features. In case you are interested in knowing other Symfony books that Packt has in store for you, here is the link: Symfony 1.3 Web Application Development, Tim Bowler, Wojciech Bancer Extending Symfony2 Web Application Framework, Sébastien Armand Resources for Article: Further resources on this subject: A Command-line Companion Called Artisan[article] Creating and Using Composer Packages[article] Services [article]
Read more
  • 0
  • 0
  • 3674

article-image-creating-photo-sharing-application
Packt
16 Jan 2015
34 min read
Save for later

Creating a Photo-sharing Application

Packt
16 Jan 2015
34 min read
In this article by Rob Foster, the author of CodeIgniter Web Application Blueprints, we will create a photo-sharing application. There are quite a few image-sharing websites around at the moment. They all share roughly the same structure: the user uploads an image and that image can be shared, allowing others to view that image. Perhaps limits or constraints are placed on the viewing of an image, perhaps the image only remains viewable for a set period of time, or within set dates, but the general structure is the same. And I'm happy to announce that this project is exactly the same. We'll create an application allowing users to share pictures; these pictures are accessible from a unique URL. To make this app, we will create two controllers: one to process image uploading and one to process the viewing and displaying of images stored. We'll create a language file to store the text, allowing you to have support for multiple languages should it be needed. We'll create all the necessary view files and a model to interface with the database. In this article, we will cover: Design and wireframes Creating the database Creating the models Creating the views Creating the controllers Putting it all together So without further ado, let's get on with it. (For more resources related to this topic, see here.) Design and wireframes As always, before we start building, we should take a look at what we plan to build. First, a brief description of our intent: we plan to build an app to allow the user to upload an image. That image will be stored in a folder with a unique name. A URL will also be generated containing a unique code, and the URL and code will be assigned to that image. The image can be accessed via that URL. The idea of using a unique URL to access that image is so that we can control access to that image, such as allowing an image to be viewed only a set number of times, or for a certain period of time only. Anyway, to get a better idea of what's happening, let's take a look at the following site map: So that's the site map. The first thing to notice is how simple the site is. There are only three main areas to this project. Let's go over each item and get a brief idea of what they do: create: Imagine this as the start point. The user will be shown a simple form allowing them to upload an image. Once the user presses the Upload button, they are directed to do_upload. do_upload: The uploaded image is validated for size and file type. If it passes, then a unique eight-character string is generated. This string is then used as the name of a folder we will make. This folder is present in the main upload folder and the uploaded image is saved in it. The image details (image name, folder name, and so on) are then passed to the database model, where another unique code is generated for the image URL. This unique code, image name, and folder name are then saved to the database. The user is then presented with a message informing them that their image has been uploaded and that a URL has been created. The user is also presented with the image they have uploaded. go: This will take a URL provided by someone typing into a browser's address bar, or an img src tag, or some other method. The go item will look at the unique code in the URL, query the database to see if that code exists, and if so, fetch the folder name and image name and deliver the image back to the method that called it. Now that we have a fairly good idea of the structure and form of the site, let's take a look at the wireframes of each page. The create item The following screenshot shows a wireframe for the create item discussed in the previous section. The user is shown a simple form allowing them to upload an image. Image2 The do_upload item The following screenshot shows a wireframe from the do_upload item discussed in the previous section. The user is shown the image they have uploaded and the URL that will direct other users to that image. The go item The following screenshot shows a wireframe from the go item described in the previous section. The go controller takes the unique code in a URL, attempts to find it in the database table images, and if found, supplies the image associated with it. Only the image is supplied, not the actual HTML markup. File overview This is a relatively small project, and all in all we're only going to create seven files, which are as follows: /path/to/codeigniter/application/models/image_model.php: This provides read/write access to the images database table. This model also takes the upload information and unique folder name (which we store the uploaded image in) from the create controller and stores this to the database. /path/to/codeigniter/application/views/create/create.php: This provides us with an interface to display a form allowing the user to upload a file. This also displays any error messages to the user, such as wrong file type, file size too big, and so on. /path/to/codeigniter/application/views/create/result.php: This displays the image to the user after it has been successfully uploaded, as well as the URL required to view that image. /path/to/codeigniter/application/views/nav/top_nav.php: This provides a navigation bar at the top of the page. /path/to/codeigniter/application/controllers/create.php: This performs validation checks on the image uploaded by the user, creates a uniquely named folder to store the uploaded image, and passes this information to the model. /path/to/codeigniter/application/controllers/go.php: This performs validation checks on the URL input by the user, looks for the unique code in the URL and attempts to find this record in the database. If it is found, then it will display the image stored on disk. /path/to/codeigniter/application/language/english/en_admin_lang.php: This provides language support for the application. The file structure of the preceding seven files is as follows: application/ ├── controllers/ │   ├── create.php │   ├── go.php ├── models/ │   ├── image_model.php ├── views/create/ │   ├── create.php │   ├── result.php ├── views/nav/ │   ├── top_nav.php ├── language/english/ │   ├── en_admin_lang.php Creating the database First, we'll build the database. Copy the following MySQL code into your database: CREATE DATABASE `imagesdb`; USE `imagesdb`;   DROP TABLE IF EXISTS `images`; CREATE TABLE `images` ( `img_id` int(11) NOT NULL AUTO_INCREMENT, `img_url_code` varchar(10) NOT NULL, `img_url_created_at` timestamp NOT NULL DEFAULT     CURRENT_TIMESTAMP, `img_image_name` varchar(255) NOT NULL, `img_dir_name` varchar(8) NOT NULL, PRIMARY KEY (`img_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; Right, let's take a look at each item in every table and see what they mean: Table: images Element Description img_id This is the primary key. img_url_code This stores the unique code that we use to identify the image in the database. img_url_created_at This is the MySQL timestamp for the record. img_image_name This is the filename provided by the CodeIgniter upload functionality. img_dir_name This is the name of the directory we store the image in. We'll also need to make amends to the config/database.php file, namely setting the database access details, username, password, and so on. Open the config/database.php file and find the following lines: $db['default']['hostname'] = 'localhost'; $db['default']['username'] = 'your username'; $db['default']['password'] = 'your password'; $db['default']['database'] = 'imagesdb'; Edit the values in the preceding code ensuring you substitute those values for the ones more specific to your setup and situation—so enter your username, password, and so on. Adjusting the config.php and autoload.php files We don't actually need to adjust the config.php file in this project as we're not really using sessions or anything like that. So we don't need an encryption key or database information. So just ensure that you are not autoloading the session in the config/autoload.php file or you will get an error, as we've not set any session variables in the config/config.php file. Adjusting the routes.php file We want to redirect the user to the create controller rather than the default CodeIgniter welcome controller. To do this, we will need to amend the default controller settings in the routes.php file to reflect this. The steps are as follows: Open the config/routes.php file for editing and find the following lines (near the bottom of the file): $route['default_controller'] = "welcome"; $route['404_override'] = ''; First, we need to change the default controller. Initially, in a CodeIgniter application, the default controller is set to welcome. However, we don't need that, instead we want the default controller to be create, so find the following line: $route['default_controller'] = "welcome"; Replace it with the following lines: $route['default_controller'] = "create"; $route['404_override'] = ''; Then we need to add some rules to govern how we handle URLs coming in and form submissions. Leave a few blank lines underneath the preceding two lines of code (default controller and 404 override) and add the following three lines of code: $route['create'] = "create/index"; $route['(:any)'] = "go/index"; $route['create/do_upload'] = "create/do_upload"; Creating the model There is only one model in this project, image_model.php. It contains functions specific to creating and resetting passwords. Create the /path/to/codeigniter/application/models/image_model.php file and add the following code to it: <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');   class Image_model extends CI_Model { function __construct() {    parent::__construct(); }   function save_image($data) {    do {       $img_url_code = random_string('alnum', 8);        $this->db->where('img_url_code = ', $img_url_code);      $this->db->from('images');      $num = $this->db->count_all_results();    } while ($num >= 1);      $query = "INSERT INTO `images` (`img_url_code`,       `img_image_name`, `img_dir_name`) VALUES (?,?,?) ";    $result = $this->db->query($query, array($img_url_code,       $data['image_name'], $data['img_dir_name']));      if ($result) {      return $img_url_code;    } else {      return flase;    } }   function fetch_image($img_url_code) {    $query = "SELECT * FROM `images` WHERE `img_url_code` = ? ";    $result = $this->db->query($query, array($img_url_code));      if ($result) {      return $result;    } else {      return false;    } } } There are two main functions in this model, which are as follows: save_image(): This generates a unique code that is associated with the uploaded image and saves it, with the image name and folder name, to the database. fetch_image(): This fetches an image's details from the database according to the unique code provided. Okay, let's take save_image() first. The save_image() function accepts an array from the create controller containing image_name (from the upload process) and img_dir_name (this is the folder that the image is stored in). A unique code is generated using a do…while loop as shown here: $img_url_code = random_string('alnum', 8); First a string is created, eight characters in length, containing alpha-numeric characters. The do…while loop checks to see if this code already exists in the database, generating a new code if it is already present. If it does not already exist, this code is used: do { $img_url_code = random_string('alnum', 8);   $this->db->where('img_url_code = ', $img_url_code); $this->db->from('images'); $num = $this->db->count_all_results(); } while ($num >= 1); This code and the contents of the $data array are then saved to the database using the following code: $query = "INSERT INTO `images` (`img_url_code`, `img_image_name`,   `img_dir_name`) VALUES (?,?,?) "; $result = $this->db->query($query, array($img_url_code,   $data['image_name'], $data['img_dir_name'])); The $img_url_code is returned if the INSERT operation was successful, and false if it failed. The code to achieve this is as follows: if ($result) { return $img_url_code; } else { return false; } Creating the views There are only three views in this project, which are as follows: /path/to/codeigniter/application/views/create/create.php: This displays a form to the user allowing them to upload an image. /path/to/codeigniter/application/views/create/result.php: This displays a link that the user can use to forward other people to the image, as well as the image itself. /path/to/codeigniter/application/views/nav/top_nav.php: This displays the top-level menu. In this project it's very simple, containing a project name and a link to go to the create controller. So those are our views, as I said, there are only three of them as it's a simple project. Now, let's create each view file. Create the /path/to/codeigniter/application/views/create/create.php file and add the following code to it: <div class="page-header"> <h1><?php echo $this->lang->line('system_system_name');     ?></h1> </div>   <p><?php echo $this->lang->line('encode_instruction_1');   ?></p>   <?php echo validation_errors(); ?>   <?php if (isset($success) && $success == true) : ?> <div class="alert alert-success">    <strong><?php echo $this->lang->line('     common_form_elements_success_notifty'); ?></strong>     <?php echo $this->lang->     line('encode_encode_now_success'); ?> </div> <?php endif ; ?>   <?php if (isset($fail) && $fail == true) : ?> <div class="alert alert-danger">    <strong><?php echo $this->lang->line('     common_form_elements_error_notifty'); ?> </strong>     <?php echo $this->lang->line('encode_encode_now_error     '); ?>    <?php echo $fail ; ?> </div> <?php endif ; ?>   <?php echo form_open_multipart('create/do_upload');?> <input type="file" name="userfile" size="20" /> <br /> <input type="submit" value="upload" /> <?php echo form_close() ; ?> <br /> <?php if (isset($result) && $result == true) : ?> <div class="alert alert-info">    <strong><?php echo $this->lang->line('     encode_upload_url'); ?> </strong>    <?php echo anchor($result, $result) ; ?> </div> <?php endif ; ?> This view file can be thought of as the main view file; it is here that the user can upload their image. Error messages are displayed here too. Create the /path/to/codeigniter/application/views/create/result.php file and add the following code to it: <div class="page-header"> <h1><?php echo $this->lang->line('system_system_name');     ?></h1> </div>   <?php if (isset($result) && $result == true) : ?>    <strong><?php echo $this->lang->line('     encode_encoded_url'); ?> </strong>    <?php echo anchor($result, $result) ; ?>    <br />    <img src="<?php echo base_url() . 'upload/' .       $img_dir_name . '/' . $file_name ;?>" /> <?php endif ; ?> This view will display the encoded image resource URL to the user (so they can copy and share it) and the actual image itself. Create the /path/to/codeigniter/application/views/nav/top_nav.php file and add the following code to it: <!-- Fixed navbar --> <div class="navbar navbar-inverse navbar-fixed-top"   role="navigation"> <div class="container">    <div class="navbar-header">      <button type="button" class="navbar-toggle" data- toggle="collapse" data-target=".navbar-collapse">        <span class="sr-only">Toggle navigation</span>        <span class="icon-bar"></span>        <span class="icon-bar"></span>        <span class="icon-bar"></span>      </button>      <a class="navbar-brand" href="#"><?php echo $this-       >lang->line('system_system_name'); ?></a>  </div>    <div class="navbar-collapse collapse">      <ul class="nav navbar-nav">        <li class="active"><?php echo anchor('create',           'Create') ; ?></li>      </ul>    </div><!--/.nav-collapse --> </div> </div>   <div class="container theme-showcase" role="main"> This view is quite basic but still serves an important role. It displays an option to return to the index() function of the create controller. Creating the controllers We're going to create two controllers in this project, which are as follows: /path/to/codeigniter/application/controllers/create.php: This handles the creation of unique folders to store images and performs the upload of a file. /path/to/codeigniter/application/controllers/go.php: This fetches the unique code from the database, and returns any image associated with that code. These are two of our controllers for this project, let's now go ahead and create them. Create the /path/to/codeigniter/application/controllers/create.php file and add the following code to it: <?php if (!defined('BASEPATH')) exit('No direct script access   allowed');   class Create extends MY_Controller { function __construct() {    parent::__construct();      $this->load->helper(array('string'));      $this->load->library('form_validation');      $this->load->library('image_lib');      $this->load->model('Image_model');      $this->form_validation->set_error_delimiters('<div         class="alert alert-danger">', '</div>');    }   public function index() {    $page_data = array('fail' => false,                        'success' => false);    $this->load->view('common/header');    $this->load->view('nav/top_nav');    $this->load->view('create/create', $page_data);    $this->load->view('common/footer'); }   public function do_upload() {    $upload_dir = '/filesystem/path/to/upload/folder/';    do {      // Make code      $code = random_string('alnum', 8);        // Scan upload dir for subdir with same name      // name as the code      $dirs = scandir($upload_dir);        // Look to see if there is already a      // directory with the name which we      // store in $code      if (in_array($code, $dirs)) { // Yes there is        $img_dir_name = false; // Set to false to begin again      } else { // No there isn't        $img_dir_name = $code; // This is a new name      }      } while ($img_dir_name == false);      if (!mkdir($upload_dir.$img_dir_name)) {      $page_data = array('fail' => $this->lang->       line('encode_upload_mkdir_error'),                          'success' => false);      $this->load->view('common/header');      $this->load->view('nav/top_nav');      $this->load->view('create/create', $page_data);      $this->load->view('common/footer');    }      $config['upload_path'] = $upload_dir.$img_dir_name;    $config['allowed_types'] = 'gif|jpg|jpeg|png';    $config['max_size'] = '10000';    $config['max_width'] = '1024';    $config['max_height'] = '768';      $this->load->library('upload', $config);      if ( ! $this->upload->do_upload()) {      $page_data = array('fail' => $this->upload->       display_errors(),                          'success' => false);      $this->load->view('common/header');      $this->load->view('nav/top_nav');      $this->load->view('create/create', $page_data);       $this->load->view('common/footer');    } else {      $image_data = $this->upload->data();      $page_data['result'] = $this->Image_model->save_image(       array('image_name' => $image_data['file_name'],         'img_dir_name' => $img_dir_name));    $page_data['file_name'] = $image_data['file_name'];      $page_data['img_dir_name'] = $img_dir_name;        if ($page_data['result'] == false) {        // success - display image and link        $page_data = array('fail' => $this->lang->         line('encode_upload_general_error'));        $this->load->view('common/header');        $this->load->view('nav/top_nav');        $this->load->view('create/create', $page_data);        $this->load->view('common/footer');      } else {        // success - display image and link        $this->load->view('common/header');        $this->load->view('nav/top_nav');        $this->load->view('create/result', $page_data);        $this->load->view('common/footer');      }    } } } Let's start with the index() function. The index() function sets the fail and success elements of the $page_data array to false. This will suppress any initial messages from being displayed to the user. The views are loaded, specifically the create/create.php view, which contains the image upload form's HTML markup. Once the user submits the form in create/create.php, the form will be submitted to the do_upload() function of the create controller. It is this function that will perform the task of uploading the image to the server. First off, do_upload() defines an initial location for the upload folder. This is stored in the $upload_dir variable. Next, we move into a do…while structure. It looks something like this: do { // something } while ('…a condition is not met'); So that means do something while a condition is not being met. Now with that in mind, think about our problem—we have to save the image being uploaded in a folder. That folder must have a unique name. So what we will do is generate a random string of eight alpha-numeric characters and then look to see if a folder exists with that name. Keeping that in mind, let's look at the code in detail: do { // Make code $code = random_string('alnum', 8);   // Scan uplaod dir for subdir with same name // name as the code $dirs = scandir($upload_dir);   // Look to see if there is already a // directory with the name which we // store in $code if (in_array($code, $dirs)) { // Yes there is    $img_dir_name = false; // Set to false to begin again } else { // No there isn't    $img_dir_name = $code; // This is a new name } } while ($img_dir_name == false); So we make a string of eight characters, containing only alphanumeric characters, using the following line of code: $code = random_string('alnum', 8); We then use the PHP function scandir() to look in $upload_dir. This will store all directory names in the $dirs variable, as follows: $dirs = scandir($upload_dir); We then use the PHP function in_array() to look for the value in $code in the list of directors from scandir(): If we don't find a match, then the value in $code must not be taken, so we'll go with that. If the value is found, then we set $img_dir_name to false, which is picked up by the final line of the do…while loop: ... } while ($img_dir_name == false); Anyway, now that we have our unique folder name, we'll attempt to create it. We use the PHP function mkdir(), passing to it $upload_dir concatenated with $img_dir_name. If mkdir() returns false, the form is displayed again along with the encode_upload_mkdir_error message set in the language file, as shown here: if (!mkdir($upload_dir.$img_dir_name)) { $page_data = array('fail' => $this->lang->   line('encode_upload_mkdir_error'),                      'success' => false); $this->load->view('common/header'); $this->load->view('nav/top_nav'); $this->load->view('create/create', $page_data); $this->load->view('common/footer'); } Once the folder has been made, we then set the configuration variables for the upload process, as follows: $config['upload_path'] = $upload_dir.$img_dir_name; $config['allowed_types'] = 'gif|jpg|jpeg|png'; $config['max_size'] = '10000'; $config['max_width'] = '1024'; $config['max_height'] = '768'; Here we are specifying that we only want to upload .gif, .jpg, .jpeg, and .png files. We also specify that an image cannot be above 10,000 KB in size (although you can set this to any value you wish—remember to adjust the upload_max_filesize and post_max_size PHP settings in your php.ini file if you want to have a really big file). We also set the minimum dimensions that an image must be. As with the file size, you can adjust this as you wish. We then load the upload library, passing to it the configuration settings, as shown here: $this->load->library('upload', $config); Next we will attempt to do the upload. If unsuccessful, the CodeIgniter function $this->upload->do_upload() will return false. We will look for this and reload the upload page if it does return false. We will also pass the specific error as a reason why it failed. This error is stored in the fail item of the $page_data array. This can be done as follows:    if ( ! $this->upload->do_upload()) {      $page_data = array('fail' => $this->upload-       >display_errors(),                          'success' => false);      $this->load->view('common/header');      $this->load->view('nav/top_nav');      $this->load->view('create/create', $page_data);      $this->load->view('common/footer');    } else { ... If, however, it did not fail, we grab the information generated by CodeIgniter from the upload. We'll store this in the $image_data array, as follows: $image_data = $this->upload->data(); Then we try to store a record of the upload in the database. We call the save_image function of Image_model, passing to it file_name from the $image_data array, as well as $img_dir_name, as shown here: $page_data['result'] = $this->Image_model-> save_image(array('image_name' => $image_data['file_name'],   'img_dir_name' => $img_dir_name)); We then test for the return value of the save_image() function; if it is successful, then Image_model will return the unique URL code generated in the model. If it is unsuccessful, then Image_model will return the Boolean false. If false is returned, then the form is loaded with a general error. If successful, then the create/result.php view file is loaded. We pass to it the unique URL code (for the link the user needs), and the folder name and image name, necessary to display the image correctly. Create the /path/to/codeigniter/application/controllers/go.php file and add the following code to it: <?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Go extends MY_Controller {function __construct() {parent::__construct();   $this->load->helper('string');} public function index() {   if (!$this->uri->segment(1)) {     redirect (base_url());   } else {     $image_code = $this->uri->segment(1);     $this->load->model('Image_model');     $query = $this->Image_model->fetch_image($image_code);      if ($query->num_rows() == 1) {       foreach ($query->result() as $row) {         $img_image_name = $row->img_image_name;         $img_dir_name = $row->img_dir_name;       }          $url_address = base_url() . 'upload/' . $img_dir_name .'/' . $img_image_name;        redirect (prep_url($url_address));      } else {        redirect('create');      }    } } } The go controller has only one main function, index(). It is called when a user clicks on a URL or a URL is called (perhaps as the src value of an HTML img tag). Here we grab the unique code generated and assigned to an image when it was uploaded in the create controller. This code is in the first value of the URI. Usually it would occupy the third parameter—with the first and second parameters normally being used to specify the controller and controller function respectively. However, we have changed this behavior using CodeIgniter routing. This is explained fully in the Adjusting the routes.php file section of this article. Once we have the unique code, we pass it to the fetch_image() function of Image_model: $image_code = $this->uri->segment(1); $this->load->model('Image_model'); $query = $this->Image_model->fetch_image($image_code); We test for what is returned. We ask if the number of rows returned equals exactly 1. If not, we will then redirect to the create controller. Perhaps you may not want to do this. Perhaps you may want to do nothing if the number of rows returned does not equal 1. For example, if the image requested is in an HTML img tag, then if an image is not found a redirect may send someone away from the site they're viewing to the upload page of this project—something you might not want to happen. If you want to remove this functionality, remove the following lines in bold from the code excerpt: ....        $img_dir_name = $row->img_dir_name;        }          $url_address = base_url() . 'upload/' . $img_dir_name .'/'           . $img_image_name;        redirect (prep_url($url_address));      } else {        redirect('create');      }    } } } .... Anyway, if the returned value is exactly 1, then we'll loop over the returned database object and find img_image_name and img_dir_name, which we'll need to locate the image in the upload folder on the disk. This can be done as follows: foreach ($query->result() as $row) { $img_image_name = $row->img_image_name; $img_dir_name = $row->img_dir_name; } We then build the address of the image file and redirect the browser to it, as follows: $url_address = base_url() . 'upload/' . $img_dir_name .'/'   . $img_image_name; redirect (prep_url($url_address)); Creating the language file We make use of the language file to serve text to users. In this way, you can enable multiple region/multiple language support. Create the /path/to/codeigniter/application/language/english/en_admin_lang.php file and add the following code to it: <?php if (!defined('BASEPATH')) exit('No direct script access   allowed');   // General $lang['system_system_name'] = "Image Share";   // Upload $lang['encode_instruction_1'] = "Upload your image to share it"; $lang['encode_upload_now'] = "Share Now"; $lang['encode_upload_now_success'] = "Your image was uploaded, you   can share it with this URL"; $lang['encode_upload_url'] = "Hey look at this, here's your   image:"; $lang['encode_upload_mkdir_error'] = "Cannot make temp folder"; $lang['encode_upload_general_error'] = "The Image cannot be saved   at this time"; Putting it all together Let's look at how the user uploads an image. The following is the sequence of events: CodeIgniter looks in the routes.php config file and finds the following line: $route['create'] = "create/index"; It directs the request to the create controller's index() function. The index() function loads the create/create.php view file that displays the upload form to the user. The user clicks on the Choose file button, navigates to the image file they wish to upload, and selects it. The user presses the Upload button and the form is submitted to the create controller's index() function. The index() function creates a folder in the main upload directory to store the image in, then does the actual upload. On a successful upload, index() sends the details of the upload (the new folder name and image name) to the save_image() model function. The save_model() function also creates a unique code and saves it in the images table along with the folder name and image name passed to it by the create controller. The unique code generated during the database insert is then returned to the controller and passed to the result view, where it will form part of a success message to the user. Now, let's see how an image is viewed (or fetched). The following is the sequence of events: A URL with the syntax www.domain.com/226KgfYH comes into the application—either when someone clicks on a link or some other call (<img src="">). CodeIgniter looks in the routes.php config file and finds the following line: $route['(:any)'] = "go/index"; As the incoming request does not match the other two routes, the preceding route is the one CodeIgniter applies to this request. The go controller is called and the code of 226KgfYH is passed to it as the 1st segment of uri. The go controller passes this to the fetch_image() function of the Image_model.php file. The fetch_image() function will attempt to find a matching record in the database. If found, it returns the folder name marking the saved location of the image, and its filename. This is returned and the path to that image is built. CodeIgniter then redirects the user to that image, that is, supplies that image resource to the user that requested it. Summary So here we have a basic image sharing application. It is capable of accepting a variety of images and assigning them to records in a database and unique folders in the filesystem. This is interesting as it leaves things open to you to improve on. For example, you can do the following: You can add limits on views. As the image record is stored in the database, you could adapt the database. Adding two columns called img_count and img_count_limit, you could allow a user to set a limit for the number of views per image and stop providing that image when that limit is met. You can limit views by date. Similar to the preceding point, but you could limit image views to set dates. You can have different URLs for different dimensions. You could add functionality to make several dimensions of image based on the initial upload, offering several different URLs for different image dimensions. You can report abuse. You could add an option allowing viewers of images to report unsavory images that might be uploaded. You can have terms of service. If you are planning on offering this type of application as an actual web service that members of the public could use, then I strongly recommend you add a terms of service document, perhaps even require that people agree to terms before they upload an image. In those terms, you'll want to mention that in order for someone to use the service, they first have to agree that they do not upload and share any images that could be considered illegal. You should also mention that you'll cooperate with any court if information is requested of you. You really don't want to get into trouble for owning or running a web service that stores unpleasant images; as much as possible you want to make your limits of liability clear and emphasize that it is the uploader who has provided the images. Resources for Article: Further resources on this subject: UCodeIgniter MVC – The Power of Simplicity! [article] Navigating Your Site using CodeIgniter 1.7: Part 1 [article] Navigating Your Site using CodeIgniter 1.7: Part 2 [article]
Read more
  • 0
  • 0
  • 3671

article-image-performing-setup-tasks-wordpress-admin-panel
Packt
02 Feb 2011
3 min read
Save for later

Performing Setup Tasks in the WordPress Admin Panel

Packt
02 Feb 2011
3 min read
  WordPress 3 Complete Create your own complete website or blog from scratch with WordPress Learn everything you need for creating your own feature-rich website or blog from scratch Clear and practical explanations of all aspects of WordPress In-depth coverage of installation, themes, plugins, and syndication Explore WordPress as a fully functional content management system Clear, easy-to-follow, concise; rich with examples and screenshots         Read more about this book       (For more resources on Wordpress, see here.) The reader can benefit from the previous article on Getting Started with WordPress 3. After you've successfully installed WordPress, it's time for our first look at the WP Admin. There are some immediate basic changes that I recommend doing right away to make sure your installation is set up properly. You can always get to the WP Admin by going to this URL: http://yoursite.com/wp-admin/. Your first time here, you'll be re-directed to the login page. In the future, WordPress will check to see if you're already logged in and, if so, you'll skip the login page. Following is the login page: To log in, just enter the username and password you chose during the installation. Then click on Log In. Note for the future that on this page there is a link you can use to retrieve your lost password. Whenever you log in, you'll be taken directly to the Dashboard of the WP Admin. Following is a screenshot of the WP Admin that you will see immediately after you log into the blog you just installed: You'll see a lot of information and options here. We will focus on the items that we need to touch upon right after a successful installation. First, let's take a brief look at the top of the WP Admin and the Dashboard. The very top bar, which I'll refer to as the top bar, is mostly a medium grey and contains: A link to the front page of your WordPress website A rollover drop-down menu with handy links to New Post, Drafts, New Page, Upload, and Comments Your username linked to your profile A link to log out You'll also notice the Screen Options tab, which appears on many screens within the WP Admin. If you click on it, it will slide down a checklist of items on the page to show or hide. It will be different on each page. I encourage you to play around with that by checking and unchecking items, as you find you need them or don't need them. On the left, of course, is the main menu: You can click on any word in the main menu to be taken to the main page for that section, or you can click on the rollover arrow to slide down the subpages for that section. For example, if you click on the arrow next to Settings, you'll see the subpages for the Settings section: The top menu and the main menu exist on every page within the WP Admin. The main section on the right contains information for the current page you're on. In this case, we're on the Dashboard. It contains boxes that have a variety of information about your blog, and about WordPress in general. Before WordPress 3, the first thing you'd have to do would be to change the password to something easier to remember. However, now that you can choose your password during installation, this is no longer necessary. Let's jump right to general site settings.  
Read more
  • 0
  • 0
  • 3670

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

Integrating Twitter with Magento

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

Checkbox Persistence in Tabular Forms (Reports)

Packt
17 Jun 2010
7 min read
(For more resources on Oracle, see here.) One of the problems we are facing with Tabular Forms is that pagination doesn't submit the current view of the Tabular Form (Report) page, and if we are using Partial Page Refresh (PPR), it doesn't even reload the entire page. As such, Session State is not saved prior to us moving to the next/previous view. Without saving Session State, all the changes that we might have made to the current form view will be lost upon using pagination. This problematic behavior is most notable when we are using a checkboxes column in our Tabular Form (Report). We can mark specific checkboxes in the current Tabular Form (Report) view, but if we paginate to another view, and then return, the marked checkboxes will be cleared (no Session State, no history to rely on). In some cases, it can be very useful to save the marked checkboxes while paginating through the Tabular Form (Report). Joel Kallman, from the APEX development team, blogged about this issue (http://joelkallman.blogspot.com/2008/03/ preserving-checked-checkboxes-in-report.html) and offered a simple solution, which uses AJAX and APEX collections. Using APEX collections means that the marked checkboxes will be preserved for the duration of a specific user's current APEX session. If that's what you need, Joel's solution is very good as it utilizes built-in APEX resources in an optimal way. However, sometimes the current APEX session is not persistent enough. In one of my applications I needed more lasting persistence, which can be used crossed APEX users and sessions. So, I took Joel's idea and modified it a bit. Instead of using APEX collections, I've decided to save the checked checkboxes into a database table. The database table, of course, can support unlimited persistence across users. Report on CUSTOMERS We are going to use a simple report on the CUSTOMERS table, where the first column is a checkboxes column. The following is a screenshot of the report region: W e are going to use AJAX to preserve the status of the checkboxes in the following scenarios: Using the checkbox in the header of the first column to check or clear all the checkboxes in the first column of the current report view Individual row check or clearing of a checkbox The first column—the checkboxes column—represents the CUST_ID column of the CUSTOMERS table, and we are going to implement persistence by saving the values of this column, for all the checked rows, in a table called CUSTOMERS_VIP. This table includes only one column: CREATE TABLE "CUSTOMERS_VIP" ( "CUST_ID" NUMBER(7,0) NOT NULL ENABLE, CONSTRAINT "CUSTOMERS_VIP_PK" PRIMARY KEY ("CUST_ID") ENABLE) Bear in mind: In this particular example we are talking about crossed APEX users and sessions persistence. If, however, you need to maintain a specific user-level persistence, as it happens natively when using APEX collections, you can add a second column to the table that can hold the APP_USER of the user. In this case, you'll need to amend the appropriate WHERE clauses and the INSERT statements, to include and reflect the second column. The report SQL query The following is the SQL code used for the report: SELECT apex_item.checkbox(10,l.cust_id,'onclick=updateCB(this);', r.cust_id) as cust_id, l.cust_name, l.cust_address1, l.cust_address2, l.cust_city, l.cust_zip_code, (select r1.sname from states r1 where l.cust_state = r1.code) state, (select r2.cname from countries r2 where l.cust_country = r2.code) countryFROM customers l, customers_vip rWHERE r.cust_id (+) = l.cust_idORDER BY cust_name The Bold segments of the SELECT statement are the ones we are most interested in. The APEX_ITEM.CHECKBOX function creates a checkboxes column in the report. Its third parameter—p_attributes—allows us to define HTML attributes within the checkbox <input> tag. We are using this parameter to attach an onclick event to every checkbox in the column. The event fires a JavaScript function— updateCB(this)—which takes the current checkbox object as a parameter and initiates an AJAX process. The fourth parameter of the APEX_ITEM.CHECKBOX function—p_checked_ values—allows us to determine the initial status of the checkbox. If the value of this parameter is equal to the value of the checkbox (determined by the second parameter—p_value) the checkbox will be checked. This parameter is the heart of the solution. Its value is taken from the CUSTOMERS_VIP table using outer join with the value of the checkbox. The outcome is that every time the CUSTOMERS_VIP table contains a CUST_ID value equal to the current checkbox value, this checkbox will be checked. The report headers In the Report Attributes tab we can set the report headers using the Custom option. We are going to use this option to set friendlier report headers, but mostly to define the first column header—a checkbox that allows us to toggle the status of all the column checkboxes. The full HTML code we are using for the header of the first column is: <input type="checkbox" id = "CB" onclick="toggleAll(this,10);"title="Mark/Clear All"> We are actually creating a checkbox, with an ID of CB and an onclick event that fires the JavaScript function toggleAll(this,10). The first parameter of this function is a reference to the checkbox object, and the second one is the first parameter—p_idx—of the APEX_ITEM.CHECKBOX function we are using to create the checkbox column. The AJAX client-side JavaScript functions So far, we have mentioned two JavaScript functions that initiate an AJAX call. The first—updateCB()—initiates an AJAX call that updates the CUSTOMERS_VIP file according to the status of a single (row) checkbox. The second one—toggleAll()— initiates an AJAX call that updates the CUSTOMERS_VIP file according to the status of the entire checkboxes column. Let's review these functions. The updateCB() JavaScript function The following is the code of this function: function updateCB(pItem){ var get = new htmldb_Get(null, $v('pFlowId'), 'APPLICATION_PROCESS=update_CB',$v('pFlowStepId')); get.addParam('x01',pItem.value); get.addParam('x02',pItem.checked); get.GetAsync(function(){return;}); get = null;} The function accepts, as a parameter, a reference to an object—this—that points to the checkbox we just clicked. We are using this reference to set the temporary item x01 to the value of the checkbox and x02 to its status (checked/unchecked). As we are using the AJ AX related temporary items, we are using the addParam() method to do so. These items will be available to us in the on-demand PL/SQL process update_CD, which implements the server-side logic of this AJAX call. We stated this process in the third parameter of the htmldb_Get constructor function— 'APPLICATION_PROCESS=update_CB'. In this example, we are using the name 'get' for the variable referencing the new instance of htmldb_Get object. The use of this name is very common in many AJAX examples, especially on the OTN APEX forum, and its related examples. As we'll see when we review the server-side logic of this AJAX call, all it does is update—insert or delete—the content of the CUSTOMERS_VIP table. As such, it doesn't have an immediate effect on the client side, and we don't need to wait for its result. This is a classic case for us to use an asynchronous AJAX call. We do so by using the GetAsync() method. In this specific case, as the client side doesn't need to process any server response, we can use an empty function as the GetAsync() parameter.
Read more
  • 0
  • 0
  • 3664

article-image-creating-new-ios-social-project
Packt
08 Oct 2013
8 min read
Save for later

Creating a New iOS Social Project

Packt
08 Oct 2013
8 min read
Creating a New iOS Social Project In this article, by Giuseppe Macri, author of Integrating Facebook iOS SDK with Your Application, we will learn about: With this article, we start our coding journey. We are going to build our social application from the group up. In this article we will learn about: Creating a Facebook App ID: It is a key used with our APIs to communicate with the Facebook Platform. Downloading the Facebook SDK: iOS SDK can be downloaded from two different channels. We will look into both of them. Creating a new XCode Project: I will give a brief introduction on how to create a new XCode project and description of the IDE environment. Importing Facebook iOS SDK into our XCode project: I will go through the import of the Facebook SDK into our XCode project step-by-step. Getting familiar with Storyboard to build a better interface: This is a brief introduction on the Apple tool to build our application interface. Creating a Facebook App ID In order to communicate with the Facebook Platform using their SDK, we need an identifier for our application. This identifier, also known as Facebook App ID, will give access to the Platform; at the same time, we will be able to collect a lot of information about its usage, impressions, and ads. To obtain a Facebook App ID, we need a Facebook account. If you don't have one, you can create a Facebook account via the following page at https://www.facebook.com: The previous screenshot shows the new Facebook account sign up form. Fill out all the fields and you will be able to access the Facebook Developer Portal. Once we are logged into Facebook, we need to visit the Developer Portal. You can find it at https://developers.facebook.com/. I already mentioned the important role of Developer Portal in developing our social application. The previous screenshot shows the Facebook Developer Portal. The main section, the top part, is dedicated to the current SDKs. On the top-blue bar, click on the Apps link, and it will redirect us to the Facebook App Dashboard. The previous screenshot shows the Facebook App Dashboard. To the left, we have a list of apps; on the center of the page, we can see the details of the currently selected app from our list. The page shows the application's setting and analytics (Insights). In order to create a new Facebook App ID, you can click on Create New App on the top-right part of the App Dashboard. The previous screenshot shows the first step in order to create a Facebook App ID. When providing the App Name, be sure the name does not already exist or violate any copyright laws; otherwise, Facebook will remove your app. App Namespace is something that we need if we want to define custom objects and/or actions in the Open Graph structure. The App Namespace topic is not part of this book. Web hosting is really useful when creating a social web application. Facebook, in partnership with other providers, can create a web hosting for us if needed. This part is not going to be discussed in this book; therefore, do not check this option for your application. Once all the information is provided, we can move on to the next step. Please fill out the form, and move forward to the next one. On the top of the page, we can see both App ID and App Secret. These are the most important pieces of information about our new social applicaton. App ID is a piece of information that we can share unlike App Secret. At the center of our new Facebook Application Page, we have basic information fields. Do not worry about Namespace, App Domains, and Hosting URL; these fields are for web applications. Sandbox Mode only allows developers to use the current application. Developers are specified through the Developer Roles link on the left side bar. Moving down, select the type of app. For our goal, select Native iOS App. You can select multiple types and create multiplatform social applications. Once you have checked Native iOS App, you will be prompted with the following form: The only field we need to provide for now is the Bundle ID. The Bundle ID is something related to XCode settings. Be sure that the Facebook Bundle ID will match our XCode Social App Bundle Identifier. The format for the bundle identifier is always something like com.MyCompany.MyApp. iPhone/iPad App Store IDs are the App Store identifiers of your application if you have published your app in the App Store. If you didn't provide any of them after you saved your changes, you will receive a warning message; however, don't worry, our new App ID is now ready to be used. Save your changes and get ready to start our developing journey. Downloading the Facebook iOS SDK The iOS Facebook SDK can be downloaded through two different channels: Facebook Developer Portal: For downloading the installation package GitHub: For downloading the SDK source code Using Facebook Developer Portal, we can download the iOS SDK as the installation package. Visit https://developers.facebook.com/ios/ as shown in the following screenshot and click on Download the SDK to download the installation package. The package, once installed, will create a new FacebookSDK folder within our Documents folder. The previous screenshot shows the content of the iOS SDK installation package. Here, we can see four elements: FacebookSDK.framework: This is the framework that we will import in our XCode social project LICENSE: It contains information about licensing and usage of the framework README: It contains all the necessary information about the framework installation Samples: It contains a useful set of sample projects that uses the iOS framework's features With the installation package, we only have the compiled files to use, with no original source code. It is possible to download the source code using the GitHub channel. To clone git repo, you will need a Git client, either Terminal or GUI. The iOS SDK framework git repo is located at https://github.com/facebook/facebook-ios-sdk.git. I prefer the Terminal client that I am using in the following command: git clone https://github.com/facebook/facebook-ios-sdk.git After we have cloned the repo, the target folder will look as the following screenshot: The previous picture shows the content of the iOS SDK GitHub repo. Two new elements are present in this repo: src and scripts. src contains the framework source code that needs to be compiled. The scripts folder has all the necessary scripts needed to compile the source code. Using the GitHub version allows us to keep the framework in our social application always up-to-date, but for the scope of this book, we will be using the installation package. Creating a new XCode project We created a Facebook App ID and downloaded the iOS Facebook SDK. It's time for us to start our social application using XCode. The application will prompt the welcome dialog if Show this window when XCode launches is enabled. Choose the Create a new XCode project option. If the welcome dialog is disabled, navigate to File | New | Project…. Choosing the type of project to work with is the next step as shown in the following screenshot: The bar to the left defines whether the project is targeting a desktop or a mobile device. Navigate to iOS | Application and choose the Single View Application project type. The previous screenshot shows our new project's details. Provide the following information for your new project: Product Name: This is the name of our application Organization Name: I will strongly recommend filling out this part even if you don't belong to an organization because this field will be part of our Bundle Identifier Company Identifier: It is still optional, but we should definitely fill it out to respect the best-practice format for Bundle Identifier Class Prefix: This prefix will be prepended to every class we are going to create in our project Devices: We can select the target device of our application; in this case, it is an iPhone but we could also have chosen iPad or Universal Use Storyboards: We are going to use storyboards to create the user interface for our application Use Automatic Reference Counting: This feature enables reference counting in the Objective C Garbage Collector Include Unit Tests: If it is selected, XCode will also create a separate project target to unit-test our app; this is not part of this book Save the new project. I will strongly recommend checking the Create a local git repository for this project option in order to keep track of changes. Once the project is under version control, we can also decide to use GitHub as the remote host to store our source code.
Read more
  • 0
  • 0
  • 3661

article-image-ecmascript-6-standard
Packt
15 Jan 2016
21 min read
Save for later

ECMAScript 6 Standard

Packt
15 Jan 2016
21 min read
In this article by Ved Antani, the author of the book Mastering JavaScript, we will learn about ECMAScript 6 standard. ECMAScript 6 (ES6) is the latest version of the ECMAScript standard. This standard is evolving and the last round of modifications was done in June, 2015. ES2015 is significant in its scope and the recommendations of ES2015 are being implemented in most JavaScript engines. This is great news. ES6 introduces a huge number of features that add syntactic forms and helpers that enrich the language significantly. The pace at which ECMAScript standards keep evolving makes it a bit difficult for browsers and JavaScript engines to support new features. It is also a practical reality that most programmers have to write code that can be supported by older browsers. The notorious Internet Explorer 6 was once the most widely used browser in the world. To make sure that your code is compatible with the most number of browsers is a daunting task. So, while you want to jump to the next set of awesome ES6 features, you will have to consider the fact that several of the ES6 features may not be supported by the most popular of browsers or JavaScript frameworks. This may look like a dire scenario, but things are not that dark. Node.js uses the latest version of the V8 engine that supports majority of ES6 features. Facebook's React also supports them. Mozilla Firefox and Google Chrome are two of the most used browsers today and they support a majority of ES6 features. To avoid such pitfalls and unpredictability, certain solutions have been proposed. The most useful among these are polyfills/shims and transpilers. (For more resources related to this topic, see here.) ES6 syntax changes ES6 brings in significant syntactic changes to JavaScript. These changes need careful study and some getting used to. In this section, we will study some of the most important syntax changes and see how you can use Babel to start using these newer constructs in your code right away. Block scoping We discussed earlier that the variables in JavaScript are function-scoped. Variables created in a nested scope are available to the entire function. Several programming languages provide you with a default block scope where any variable declared within a block of code (usually delimited by {}) is scoped (available) only within this block. To achieve a similar block scope in JavaScript, a prevalent method is to use immediately-invoked function expressions (IIFE). Consider the following example: var a = 1; (function blockscope(){     var a = 2;     console.log(a);   // 2 })(); console.log(a);       // 1 Using the IIFE, we are creating a block scope for the a variable. When a variable is declared in the IIFE, its scope is restricted within the function. This is the traditional way of simulating the block scope. ES6 supports block scoping without using IIFEs. In ES6, you can enclose any statement(s) in a block defined by {}. Instead of using var, you can declare a variable using let to define the block scope. The preceding example can be rewritten using ES6 block scopes as follows: "use strict"; var a = 1; {   let a = 2;   console.log( a ); // 2 } console.log( a ); // 1 Using standalone brackets {} may seem unusual in JavaScript, but this convention is fairly common to create a block scope in many languages. The block scope kicks in other constructs such as if { } or for (){ } as well. When you use a block scope in this way, it is generally preferred to put the variable declaration on top of the block. One difference between variables declared using var and let is that variables declared with var are attached to the entire function scope, while variables declared using let are attached to the block scope and they are not initialized until they appear in the block. Hence, you cannot access a variable declared with let earlier than its declaration, whereas with variables declared using var, the ordering doesn't matter: function fooey() {   console.log(foo); // ReferenceError   let foo = 5000; } One specific use of let is in for loops. When we use a variable declared using var in a for loop, it is created in the global or parent scope. We can create a block-scoped variable in the for loop scope by declaring a variable using let. Consider the following example: for (let i = 0; i<5; i++) {   console.log(i); } console.log(i); // i is not defined As i is created using let, it is scoped in the for loop. You can see that the variable is not available outside the scope. One more use of block scopes in ES6 is the ability to create constants. Using the const keyword, you can create constants in the block scope. Once the value is set, you cannot change the value of such a constant: if(true){   const a=1;   console.log(a);   a=100;  ///"a" is read-only, you will get a TypeError } A constant has to be initialized while being declared. The same block scope rules apply to functions also. When a function is declared inside a block, it is available only within that scope. Default parameters Defaulting is very common. You always set some default value to parameters passed to a function or variables that you initialize. You may have seen code similar to the following: function sum(a,b){   a = a || 0;   b = b || 0;   return (a+b); } console.log(sum(9,9)); //18 console.log(sum(9));   //9 Here, we are using || (OR operator) to default variables a and b to 0 if no value was supplied when this function was invoked. With ES6, you have a standard way of defaulting function arguments. The preceding example can be rewritten as follows: function sum(a=0, b=0){   return (a+b); } console.log(sum(9,9)); //18 console.log(sum(9));   //9 You can pass any valid expression or function call as part of the default parameter list. Spread and rest ES6 has a new operator, …. Based on how it is used, it is called either spread or rest. Let's look at a trivial example: function print(a, b){   console.log(a,b); } print(...[1,2]);  //1,2 What's happening here is that when you add … before an array (or an iterable) it spreads the element of the array in individual variables in the function parameters. The a and b function parameters were assigned two values from the array when it was spread out. Extra parameters are ignored while spreading an array: print(...[1,2,3 ]);  //1,2 This would still print 1 and 2 because there are only two functional parameters available. Spreads can be used in other places also, such as array assignments: var a = [1,2]; var b = [ 0, ...a, 3 ]; console.log( b ); //[0,1,2,3] There is another use of the … operator that is the very opposite of the one that we just saw. Instead of spreading the values, the same operator can gather them into one: function print (a,...b){   console.log(a,b); } console.log(print(1,2,3,4,5,6,7));  //1 [2,3,4,5,6,7] In this case, the variable b takes the rest of the values. The a variable took the first value as 1 and b took the rest of the values as an array. Destructuring If you have worked on a functional language such as Erlang, you will relate to the concept of pattern matching. Destructuring in JavaScript is something very similar. Destructuring allows you to bind values to variables using pattern matching. Consider the following example: var [start, end] = [0,5]; for (let i=start; i<end; i++){   console.log(i); } //prints - 0,1,2,3,4 We are assigning two variables with the help of array destructuring: var [start, end] = [0,5]; As shown in the preceding example, we want the pattern to match when the first value is assigned to the first variable (start) and the second value is assigned to the second variable (end). Consider the following snippet to see how the destructuring of array elements works: function fn() {   return [1,2,3]; } var [a,b,c]=fn(); console.log(a,b,c); //1 2 3 //We can skip one of them var [d,,f]=fn(); console.log(d,f);   //1 3 //Rest of the values are not used var [e,] = fn(); console.log(e);     //1 Let's discuss how objects' destructuring works. Let's say that you have a function f that returns an object as follows: function f() {   return {     a: 'a',     b: 'b',     c: 'c'   }; } When we destructure the object being returned by this function, we can use the similar syntax as we saw earlier; the difference is that we use {} instead of []: var { a: a, b: b, c: c } = f(); console.log(a,b,c); //a b c Similar to arrays, we use pattern matching to assign variables to their corresponding values returned by the function. There is an even shorter way of writing this if you are using the same variable as the one being matched. The following example would do just fine: var { a,b,c } = f(); However, you would mostly be using a different variable name from the one being returned by the function. It is important to remember that the syntax is source: destination and not the usual destination: source. Carefully observe the following example: //this is target: source - which is incorrect var { x: a, x: b, x: c } = f(); console.log(x,y,z); //x is undefined, y is undefined z is undefined //this is source: target - correct var { a: x, b: y, c: z } = f(); console.log(x,y,z); // a b c This is the opposite of the target = source way of assigning values and hence will take some time in getting used to. Object literals Object literals are everywhere in JavaScript. You would think that there is no scope of improvement there. However, ES6 wants to improve this too. ES6 introduces several shortcuts to create a concise syntax around object literals: var firstname = "Albert", lastname = "Einstein",   person = {     firstname: firstname,     lastname: lastname   }; If you intend to use the same property name as the variable that you are assigning, you can use the concise property notation of ES6: var firstname = "Albert", lastname = "Einstein",   person = {     firstname,     lastname   }; Similarly, you are assigning functions to properties as follows: var person = {   getName: function(){     // ..   },   getAge: function(){     //..   } } Instead of the preceding lines, you can say the following: var person = {   getName(){     // ..   },   getAge(){     //..   } } Template literals I am sure you have done things like the following: function SuperLogger(level, clazz, msg){   console.log(level+": Exception happened in class:"+clazz+" -     Exception :"+ msg); } This is a very common way of replacing variable values to form a string literal. ES6 provides you with a new type of string literal using the backtick (`) delimiter. You can use string interpolation to put placeholders in a template string literal. The placeholders will be parsed and evaluated. The preceding example can be rewritten as follows: function SuperLogger(level, clazz, msg){   console.log(`${level} : Exception happened in class: ${clazz} -     Exception : {$msg}`); } We are using `` around a string literal. Within this literal, any expression of the ${..} form is parsed immediately. This parsing is called interpolation. While parsing, the variable's value replaces the placeholder within ${}. The resulting string is just a normal string with the placeholders replaced with actual variable values. With string interpolation, you can split a string into multiple lines also, as shown in the following code (very similar to Python): var quote = `Good night, good night! Parting is such sweet sorrow, that I shall say good night till it be morrow.`; console.log( quote ); You can use function calls or valid JavaScript expressions as part of the string interpolation: function sum(a,b){   console.log(`The sum seems to be ${a + b}`); } sum(1,2); //The sum seems to be 3 The final variation of the template strings is called tagged template string. The idea is to modify the template string using a function. Consider the following example: function emmy(key, ...values){   console.log(key);   console.log(values); } let category="Best Movie"; let movie="Adventures in ES6"; emmy`And the award for ${category} goes to ${movie}`;   //["And the award for "," goes to ",""] //["Best Movie","Adventures in ES6"] The strangest part is when we call the emmy function with the template literal. It's not a traditional function call syntax. We are not writing emmy(); we are just tagging the literal with the function. When this function is called, the first argument is an array of all the plain strings (the string between interpolated expressions). The second argument is the array where all the interpolated expressions are evaluated and stored. Now what this means is that the tag function can actually change the resulting template tag: function priceFilter(s, ...v){   //Bump up discount   return s[0]+ (v[0] + 5); } let default_discount = 20; let greeting = priceFilter `Your purchase has a discount of   ${default_discount} percent`; console.log(greeting);  //Your purchase has a discount of 25 As you can see, we modified the value of the discount in the tag function and returned the modified values. Maps and Sets ES6 introduces four new data structures: Map, WeakMap, Set, and WeakSet. We discussed earlier that objects are the usual way of creating key-value pairs in JavaScript. The disadvantage of objects is that you cannot use non-string values as keys. The following snippets demonstrate how Maps are created in ES6: let m = new Map(); let s = { 'seq' : 101 };   m.set('1','Albert'); m.set('MAX', 99); m.set(s,'Einstein');   console.log(m.has('1')); //true console.log(m.get(s));   //Einstein console.log(m.size);     //3 m.delete(s); m.clear(); You can initialize the map while declaring it: let m = new Map([   [ 1, 'Albert' ],   [ 2, 'Douglas' ],   [ 3, 'Clive' ], ]); If you want to iterate over the entries in the Map, you can use the entries() function that will return you an iterator. You can iterate through all the keys using the keys() function and you can iterate through the values of the Map using values() function: let m2 = new Map([     [ 1, 'Albert' ],     [ 2, 'Douglas' ],     [ 3, 'Clive' ], ]); for (let a of m2.entries()){   console.log(a); } //[1,"Albert"] [2,"Douglas"][3,"Clive"] for (let a of m2.keys()){   console.log(a); } //1 2 3 for (let a of m2.values()){   console.log(a); } //Albert Douglas Clive A variation of JavaScript Maps is a WeakMap—a WeakMap does not prevent its keys from being garbage-collected. Keys for a WeakMap must be objects and the values can be arbitrary values. While a WeakMap behaves in the same way as a normal Map, you cannot iterate through it and you can't clear it. There are reasons behind these restrictions. As the state of the Map is not guaranteed to remain static (keys may get garbage-collected), you cannot ensure correct iteration. There are not many cases where you may want to use WeakMap. The most uses of a Map can be written using normal Maps. While Maps allow you to store arbitrary values, Sets are a collection of unique values. Sets have similar methods as Maps; however, set() is replaced with add(), and the get() method does not exist. The reason that the get() method is not there is because a Set has unique values, so you are interested in only checking whether the Set contains a value or not. Consider the following example: let x = {'first': 'Albert'}; let s = new Set([1,2,'Sunday',x]); //console.log(s.has(x));  //true s.add(300); //console.log(s);  //[1,2,"Sunday",{"first":"Albert"},300]   for (let a of s.entries()){   console.log(a); } //[1,1] //[2,2] //["Sunday","Sunday"] //[{"first":"Albert"},{"first":"Albert"}] //[300,300] for (let a of s.keys()){   console.log(a); } //1 //2 //Sunday //{"first":"Albert"} //300 for (let a of s.values()){   console.log(a); } //1 //2 //Sunday //{"first":"Albert"} //300 The keys() and values() iterators both return a list of the unique values in the Set. The entries() iterator yields a list of entry arrays, where both items of the array are the unique Set values. The default iterator for a Set is its values() iterator. Symbols ES6 introduces a new data type called Symbols. A Symbol is guaranteed to be unique and immutable. Symbols are usually used as an identifier for object properties. They can be considered as uniquely generated IDs. You can create Symbols with the Symbol() factory method—remember that this is not a constructor and hence you should not use a new operator: let s = Symbol(); console.log(typeof s); //symbol Unlike strings, Symbols are guaranteed to be unique and hence help in preventing name clashes. With Symbols, we have an extensibility mechanism that works for everyone. ES6 comes with a number of predefined built-in Symbols that expose various meta behaviors on JavaScript object values. Iterators Iterators have been around in other programming languages for quite some time. They give convenience methods to work with collections of data. ES6 introduces iterators for the same use case. ES6 iterators are objects with a specific interface. Iterators have a next() method that returns an object. The returning object has two properties—value (the next value) and done (indicates whether the last result has been reached). ES6 also defines an Iterable interface, which describes objects that must be able to produce iterators. Let's look at an array, which is an iterable, and the iterator that it can produce to consume its values: var a = [1,2]; var i = a[Symbol.iterator](); console.log(i.next());      // { value: 1, done: false } console.log(i.next());      // { value: 2, done: false } console.log(i.next());      // { value: undefined, done: true } As you can see, we are accessing the array's iterator via Symbol.iterator() and calling the next() method on it to get each successive element. Both value and done are returned by the next() method call. When you call next() past the last element in the array, you get an undefined value and done: true indicating that you have iterated over the entire array. For..of loops ES6 adds a new iteration mechanism in form of for..of loop, which loops over the set of values produced by an iterator. The value that we iterate over with for..of is an iterable. Let's compare for..of to for..in: var list = ['Sunday','Monday','Tuesday']; for (let i in list){   console.log(i);  //0 1 2 } for (let i of list){   console.log(i);  //Sunday Monday Tuesday } As you can see, using the for  in loop, you can iterate over indexes of the list array, while the for..of loop lets you iterate over the values stored in the list array. Arrow functions One of the most interesting new parts of ECMAScript 6 is arrow functions. Arrow functions are, as the name suggests, functions defined with a new syntax that uses an arrow (=>) as part of the syntax. Let's first see how arrow functions look: //Traditional Function function multiply(a,b) {   return a*b; } //Arrow var multiply = (a,b) => a*b; console.log(multiply(1,2)); //2 The arrow function definition consists of a parameter list (of zero or more parameters and surrounding ( .. ) if there's not exactly one parameter), followed by the => marker, which is followed by a function body. The body of the function can be enclosed by { .. } if there's more than one expression in the body. If there's only one expression, and you omit the surrounding { .. }, there's an implied return in front of the expression. There are several variations of how you can write arrow functions. The following are the most commonly used: // single argument, single statement //arg => expression; var f1 = x => console.log("Just X"); f1(); //Just X   // multiple arguments, single statement //(arg1 [, arg2]) => expression; var f2 = (x,y) => x*y; console.log(f2(2,2)); //4   // single argument, multiple statements // arg => { //     statements; // } var f3 = x => {   if(x>5){     console.log(x);   }   else {     console.log(x+5);   } } f3(6); //6   // multiple arguments, multiple statements // ([arg] [, arg]) => { //   statements // } var f4 = (x,y) => {   if(x!=0 && y!=0){     return x*y;   } } console.log(f4(2,2));//4   // with no arguments, single statement //() => expression; var f5 = () => 2*2; console.log(f5()); //4   //IIFE console.log(( x => x * 3 )( 3 )); // 9 It is important to remember that all the characteristics of a normal function parameters are available to arrow functions, including default values, destructuring, and rest parameters. Arrow functions offer a convenient and short syntax, which gives your code a very functional programming flavor. Arrow functions are popular because they offer an attractive promise of writing concise functions by dropping function, return, and { .. } from the code. However, arrow functions are designed to fundamentally solve a particular and common pain point with this-aware coding. In normal ES5 functions, every new function defined its own value of this (a new object in case of a constructor, undefined in strict mode function calls, context object if the function is called as an object method, and so on). JavaScript functions always have their own this and this prevents you from accessing the this of, for example, a surrounding method from inside a callback. To understand this problem, consider the following example: function CustomStr(str){   this.str = str; } CustomStr.prototype.add = function(s){   // --> 1   'use strict';   return s.map(function (a){             // --> 2     return this.str + a;                 // --> 3   }); };   var customStr = new CustomStr("Hello"); console.log(customStr.add(["World"])); //Cannot read property 'str' of undefined On the line marked with 3, we are trying to get this.str, but the anonymous function also has its own this, which shadows this from the method from line 1. To fix this in ES5, we can assign this to a variable and use the variable instead: function CustomStr(str){   this.str = str; } CustomStr.prototype.add = function(s){     'use strict';   var that = this;                       // --> 1   return s.map(function (a){             // --> 2     return that.str + a;                 // --> 3   }); };   var customStr = new CustomStr("Hello"); console.log(customStr.add(["World"])); //["HelloWorld] On the line marked with 1, we are assigning this to a variable, that, and in the anonymous function, we are using the that variable, which will have a reference to this from the correct context. ES6 arrow functions have lexical this, meaning that the arrow functions capture the this value of the enclosing context. We can convert the preceding function to an equivalent arrow function as follows: function CustomStr(str){   this.str = str; } CustomStr.prototype.add = function(s){   return s.map((a)=> {     return this.str + a;   }); }; var customStr = new CustomStr("Hello"); console.log(customStr.add(["World"])); //["HelloWorld] Summary In this article, we discussed a few important features being added to the language in ES6. It's an exciting collection of new language features and paradigms, and using polyfills and transpilers, you can start with them right away. JavaScript is an ever growing language and it is important to understand what the future holds. ES6 features make JavaScript an even more interesting and mature language. Resources for Article: Further resources on this subject: Using JavaScript with HTML [article] Getting Started with Tableau Public [article] Façade Pattern – Being Adaptive with Façade [article]
Read more
  • 0
  • 0
  • 3658

article-image-moodle-19-math-quizzes-part-1
Packt
30 Nov 2009
5 min read
Save for later

Moodle 1.9 Math Quizzes: Part 1

Packt
30 Nov 2009
5 min read
As good as the Moodle quiz module is at recognizing the correctness of our students' answers, we quickly run into problems when we need Moodle to recognize, for example, that 3a+2b is exactly the same as 2b+3a . To accomplish this, we're going to need a Computer Algebra System (CAS). The Maxima system (more on this later) has been successfully integrated into Moodle, thanks to the work carried out by Chris Sangwin and Alex Billingsley at the University of Birmingham in the UK. In this article, we will also learn how to perform these tasks: Install and integrate STACK into Moodle Create questions that can be automatically marked using STACK Let's start by adding numeric questions into the course question bank. Creating quizzes Creating a quiz in Moodle is a two-stage process. First, we add our questions to the question bank (each course has its own question bank). Once we've added questions to the question bank, we can add a quiz activity to the course and then choose questions to add to it from the question bank. What are the advantages of having a two-stage process? I worked in much the same way creating quizzes before I started with Moodle. My bookshelf of math books was my question bank, and I would take questions from there to add into my quizzes. Here are just a few of the advantages: If there is a particular point you want to reinforce, then it's easy to include the same question in different quizzes throughout your course. It's easy to share your questions with other Moodle courses. For example, questions on the Pythagorean Theorem are relevant to pure math, mechanics, engineering, and physics. Questions can be exported from and imported into the question bank. This means converting questions over to Moodle is a job that can be shared between colleagues. Here's a basic Pythagorean Theorem question I converted over to Moodle: Question types However, I don't want to convert just this single question over to Moodle; I also want to have questions similar to this one but with different numbers. I want those numbers chosen randomly by Moodle, so I don't have to keep thinking up different numbers each time I set the quiz. The question type I need is Calculated, which we'll learn about in the next section. Calculated question type Let's learn how to add a calculated question to the course question bank now: Return to your course's front page, and click on Questions in the course Administration block: The course Question bank is displayed. From the Create new question drop-down menu, choose Calculated: Give the question a name. Make sure it's a name that you (and, potentially, your colleagues) can recognize when it's in the question bank. Don't call it '1', 'i', or 'a)' because you don't know where it will appear in the quiz. Now, supply the question text: Notice that I have used placeholders in the text, {a} and {b}. We will be configuring Moodle to replace those with numbers shortly. Scroll down to the Answer box. We need to enter the correct calculation into the Correct Answer Formula edit box (don't include a '=' in your answer): The students need to give the correct answer (exactly), but don't worry about the Tolerance setting: leave it set to 0.01. Set the Grade to 100%. I want the students to give their answers to three significant figures, and to that end I needed to click on the Correct answer shows drop-down menu, set that to 3, and change the Format to significant figures: Scroll down to the bottom of the page, and click on the Next Page button. You are now taken to the Choose dataset properties page. The numbers for the variables {a} and {b} will be chosen from a dataset. I want to use my own datasets for each variable. Select will use a new shared dataset for both drop-downs: Click on the Next Page button. You are taken to the Edit the datasets page. Now, we can specify the range of values for {a} and {b}: We need to add numbers to this dataset. I want to add 20 possible pairs of numbers for {a} and {b}. Scroll down to the Add box, select 20 items from the item(s) drop-down menu and click on the Add button: Twenty pairs of numbers are now added to the dataset. Moodle will choose pairs of numbers in this dataset when the student is presented with the question. If you want to alter any of the numbers Moodle has automatically generated for us, you can do so in the second-half of the page. Scroll down to the very bottom of the page, and click on the Save changes button. Our new calculated question is now added to the question bank: To recap, we have seen that creating a calculated question is a two-step process. First, we need to specify the question text. The question text contains variables that Moodle will then replace with random values when the quiz is taken. Then, we need to specify datasets for each of the variables, from which Moodle will choose the values when the quiz is taken. We can have Moodle choose the numbers for us, or we can select our own.
Read more
  • 0
  • 0
  • 3658
article-image-jasperreports-36-creating-simple-one-page-toc-your-report
Packt
30 Jun 2010
3 min read
Save for later

JasperReports 3.6: Creating a Simple, One-page TOC for Your Report

Packt
30 Jun 2010
3 min read
(For more resources on JasperReports, see here.) Getting ready Refer to the installPostgreSQL.txt file included in the source code download (chap5) to install and run PostgreSQL, which should be up and running before you proceed. The source code also includes a file named copySampleDataIntoPGS.txt, which helps you create a database named jasperdb6 and copy sample data for this recipe into the database. How to do it... Open the SimpleTOCReport.jrxml file from the Task2 folder of the source code. The Designer tab of iReport shows a report containing data in Title, Column Header, Customer Group Header 1, Product Group Header 1, Detail 1, and Product Group Footer 1 sections, as shown in the following screenshot: Switch to the Preview tab and you will see invoices for each customer grouped by product names. Switch back to the Designer tab. Right-click on the Variables node in the Report Inspector window on the left side of your report. From the pop-up menu that appears, select the Add Variable option. A new variable named variable1 will be added at the end of the variables list. While variable1 is selected, find the Name property in the Properties window below the Palette of components and change its value to FirstRecordOfANewGroup. Now the name of the variable1 variable will change to FirstRecordOfANewGroup. Select the Variable Class property and change its value to java.lang.Integer. Select the Calculation property and change its value to Count. Select the Reset type property and change its value to Group. Select the Reset group property and change its value to Customer. Select the Variable Expression property and click the button beside it. A Variable Expression window with no default expression will open, as shown in the next screenshot: Select Variables in the first column of the lower-half of the Variable Expression window. Then double-click the FirstRecordOfANewGroup variable in the second column. A new expression $V{FirstRecordOfANewGroup} will appear in the Variable Expression window, as shown in the next screenshot. Press the OK button. Right-click on the Variables node in the Report Inspector window. A pop-up menu will appear. Select the Add Variable option. A new variable named variable1 will be added at the end of the variables list. While variable1 is selected, find the Name property in the Properties window below the Palette of components and change its value to TOC. Now the name of the variable1 variable will change to TOC. Select the Variable Class property and change its value to java.lang.String.
Read more
  • 0
  • 0
  • 3651

article-image-using-web-pages-upk-35
Packt
16 Nov 2009
12 min read
Save for later

Using Web Pages in UPK 3.5

Packt
16 Nov 2009
12 min read
Using Web Pages in the Concept pane The most common use of Web Pages is to provide context information for Topics. Look at the following image of the Outline for our example course: You will see that the upper-right section of the Outline window contains a pane labeled Concept. If you want any information to be displayed in this pane, then you need to create a Web Page and attach it to the content object in the Outline. Version Difference Although the Concept pane always appears in the Outline view, if it is empty, then it does not appear in the Player. This is, thankfully, a new feature in UPK 3.5. In previous versions, the Concept pane always appeared in the Player, often appearing as a blank frame, where developers couldn't be bothered to provide any concepts. To create a new Web Page and attach it to a content object, carry out the following steps: Open the Outline containing the content object to which you want to attach the Web Page, in the Outline Editor. Click on the content object to select it. Although in this example we are attaching a Web Page to the concept pane for a Topic, Modules, and Sections also have concept panes, so you can also attach Web Pages to these as well. Click on the Create new web page button () in the Concept pane. The Save As dialog box is displayed. Navigate to the folder in which you want to save the Web Page (we will use an Assets sub-folder for all of our Web Pages), enter a name for the Web Page (it makes sense to use a reference to the content object to which the Web Page relates), and then click on the Save button. The Web Page Editor is opened on the Developer screen, as shown in the next screenshot: Enter the information that you want to appear in the Concept pane in the editor (as has already been done in the previous example). You can set the font face and size; set text to bold, italics, and underlined; change the text color, and change change the background color (again, as has been done in the earlier example). You can also change the paragraph alignment, and format numbered and bulleted lists. Once you have entered the required text, click on the Save button () to save your changes, and then close the Web Page Editor tabbed page. You are returned to the Outline Editor. Now, the Concept pane shows the contents of the Web Page, as shown in the next screenshot: Version Difference In UPK 3.5 (and OnDemand 9.1) you can only attach a single Web Page to the Concept pane. This is a change from OnDemand 8.7, where you could attach multiple Infoblocks and they would be displayed sequentially. (But note that if you convert content from OnDemand 8.7 where there are multiple Infoblocks in a single Concept pane, then all of the attached Infoblocks will be converted to a single Web Page in UPK 3.5.) The above steps explain how to attach a Web Page to the Concept pane for an exercise from the Outline. Although this is done from the Outline, the Web Page is attached to the Topic content object and not to the outline element. If you subsequently insert the same Topic in another Outline, the same Web Page will be used in the Concept pane of the new Outline. You can also attach a Web Page to the Concept pane for an exercise from within the Topic Editor. This is a useful option if you want to create concept Web Page but have not yet built an Outline to house the Topic. To do this, follow these steps: Open the Topic in the Topic Editor. Select menu option View|Concept Properties. The Concept Properties dialog box is displayed. This is very similar to the Concept pane seen in the Overview. It contains the same buttons. Create and save the Web Page as described above. When you have finished, and the Web Page is displayed in the Concept Properties dialog box, click on the OK button. Using images in Web Pages As stated above, a Web Page can contain an image. This can be instead of, or in addition to, any text (although if you only wanted to include a single image in the Web Page you could always use a Package, as explained later in this article). Images are a nice way of adding interest to a Web Page (and therefore to your training), or of providing additional information that is better explained graphically (after all, a picture is worth a thousand words). However, if you are using images in the Concept pane, then you should consider the overall size of the image and the likely width of the Concept pane, bearing in mind that the trainee may run the Player in a smaller window than the one you design. For our sample exercise, given that we are providing simulations for the SAP system, we will include a small SAP logo in the Web Page that appears in the Concept pane for our course Module. For the sake of variety, we will do this from the Library, and not from the Outline Editor. To add an image to a Web Page, carry out the steps described below. In the Library, locate the folder containing the Web Page to which you want to add the image. Double-click on the Web Page, to open it in the Web Page Editor. As before, this is opened in a separate tab in the main UPK Developer window, as can be seen in the next screenshot: Within the Web Page, position the cursor at the place in the text where you want the image to appear. Select menu option Insert|Image. The Insert Image dialog box is displayed, as shown in the next screenshot: In the Link to bar on the leftmost side of the dialog box, select the location of the image file that you want to insert into the Web Page. You can insert an image that you have already imported into your Library (for example, in a Package), an image that is located on your computer (or any attached drive) (option Image on My Computer), or an image from the Internet (option URL). For our sample exercise, we will insert an image from our computer. In the rightmost side of the dialog box, navigate to and select the image file that you want to insert into the Web Page. Click on OK. The image is inserted, as shown in the following screenshot: Save the Web Page, and close the Web Page Editor, if you have finished editing this Web Page. (We have not; we want to adjust the image, as explained below). Of course, this doesn't look too pretty. Thankfully, we can do something about this, because UPK provides some rudimentary features for adjusting images in Web Pages. To adjust the size or position of an image in a Web Page, carry out the following steps: With the Web Page open in the Web Page Editor, right-click on the image that you want to adjust, and then select Image Properties from the context menu. The Image Properties dialog box is displayed, as shown in the next screenshot: In the Alternative Text field, enter a short text description of the image. This will be used as the ToolTip in some web browsers. Under Size, select whether you want to use the original image size or not, and specify a new height and width if you choose not. Under Appearance, select a border width and indicate where the image should be aligned on the Web Page. Your choices are: Top, Middle, Bottom, Left, and Right. The first three of these (the vertical options) control the position of the image relative to the line of text in which it is located. The last two (the horizontal options) determine whether the image is left-aligned or right-aligned within the overall Web Page. Although these are two independent things (vertical and horizontal), you can only select one, so if you want the image to be right-aligned and halfway down the page, you can't do it. Click on OK to confirm your changes. Save and close the Web Page. For our sample exercise, we resized the image, set it to be right-aligned, and added a 1pt border around it (because it looked odd with a white background against the blue of the Web Page, without a border). A better option would be to use an image with a transparent background. In this example we have used an image with a solid background just for the purposes of illustration. These settings are as shown in the previous screenshot. This gives us a final Web Page as shown in the next screenshot. Note that this screenshot is taken from the Player, so that you can see how images are handled in the Player. Note that the text flows around the image. You will see that there is a little more space to the right of the image than there is above it. This is to leave room for a scrollbar. Creating independent Web Pages In the previous section, we looked at how to use a Web Page to add information to the Concept pane of a content object. In this section, we will look at how to use Web Pages to provide information in other areas. Observant readers will have noticed that a Web Page is in fact an independent content object itself. When you created a Web Page to attach to a Concept pane, you edited this Web Page in its own tabbed editor and saved it to its own folder (our Assets folder). Hopefully, you also noticed that in addition to the Create new web page button (), the Concept pane also has a Create link button () that can be used to attach an existing Web Page to the Concept pane. It should, therefore, come as no surprise to learn that Web Pages can be created independently of the Concept pane. In fact, the Concept pane is only one of several uses of Web Pages. To create a Web Page that is independent of a Concepts pane (or anything else), carry out these steps. In the Library, navigate to the folder in which you want to store the Web Page. In our example, we are saving all of the Web Pages for a module in a sub-folder called Assets within the course folder. Select menu option File|New|Web Page. A Web Page Editor tabbed page is opened up on the Developer screen. The content and use of this is exactly as described above in the explanation of how to create a Web Page from within an Outline. Enter the required information into the Web Page, and format it as required. We have already covered most of the available options, above. Once you have made your changes, click on the Save button (). You will be prompted to select the destination folder (which will default to the folder selected in Step 1, although you can change this) and a file name. Refer to the description of the Save As dialog box above for additional help if necessary. Close the Web Page Editor. You have now created a new Web Page. Now let's look at how to use it. Using Web Pages in Topics If you recall our long-running exercise on maintaining your SAP user profile, you will remember that we ask the user to enter their last name and their first name. These terms may be confusing in some countries—especially in countries where the "family name" actually comes before the "given name"—so we want to provide some extra explanation of some common name formats in different countries, and how these translate into first name and last name. We'll provide this information in a Web Page, and then link to this Web Page at the relevant place(s) within our Topic. First, we need to create the Web Page. How to do this is explained in the section Creating independent Web Pages, above. For our exercise, our created Web Page is as follows: There are two ways in which you can link to a Web Page from a Topic. These are explained separately, below. Linking via a hyperlink With a hyperlink, the Web Page is linked from a word or phrase within the Bubble Text of a Frame. (Note that it is only possible to do this for Custom Text because you can't select the Template Text to hyperlink from.) To create a hyperlink to a Web Page from within a Frame in a Topic, carry out the steps described below: Open up the Topic in the Topic Editor. Navigate to the Frame from which you want to provide the hyperlink. In our exercise, we will link from the Explanation Frame describing the Last name field (this is Frame 5B). In the Bubble Properties pane, select the text that you want to form the hyperlink (that is, the text that the user will click on to display the Web Page). Click on the Bubble text link button () in the Bubble Properties pane. The Bubble Text Link Properties dialog box is displayed. Click on the Create link button () to create a link to an existing Web Page (you could also click on the Create new web page button () to create a new Web Page if you have not yet created it). The Insert Hyperlink dialog box is displayed, as shown in the next screenshot: Make sure that the Document in Library option is selected in the Link to: bar. In the Look in: field, navigate to the folder containing the Web Page (the Assets folder, in our example). In the file list, click on the Web Page to which you want to create a hyperlink. Click on the Open button. Back in the Bubble Text Link Properties dialog box, click on OK. This hyperlink will now appear as follows, in the Player: Note that there is no ToolTip for this hyperlink. There was no opportunity to enter one in the steps above, so UPK doesn't know what to use. Version Difference In OnDemand 8.7 the Infoblock name was used as the ToolTip, but this is not the case from OnDemand 9.x onwards.
Read more
  • 0
  • 0
  • 3648

article-image-how-create-image-gallery-wordpress-3
Packt
31 Jan 2011
5 min read
Save for later

How to Create an Image Gallery in WordPress 3

Packt
31 Jan 2011
5 min read
  WordPress 3 Complete Create your own complete website or blog from scratch with WordPress Learn everything you need for creating your own feature-rich website or blog from scratch Clear and practical explanations of all aspects of WordPress In-depth coverage of installation, themes, plugins, and syndication Explore WordPress as a fully functional content management system Clear, easy-to-follow, concise; rich with examples and screenshots         Read more about this book       (For more resources on Wordpress, see here.) Let's get started. Choosing a post or page For my food blog, I'm going to create a new page named My Food Photos for my image gallery. You can always do this on an existing page or post. Following is my new page: Note where I have left my cursor. I made sure to leave it in a spot on the page where I want my gallery to appear, that is, underneath my introductory text (After creating this page, I will also navigate to Appearance | Menus to add it as a subpage under About.). Uploading images Now click on the Upload/Insert image icon and upload some photos (you can choose multiple photos at once). For each photo you upload, enter the title (and a caption if you'd like). When you're done, click on the Save All Changes button. You'll be taken to the Gallery tab, which will show all of the photos you've uploaded to be attached to this page: If you want to upload more photos at this point, just click on the From Computer tab at the top, and upload another photo. When you've uploaded all the photos you want (you can add more later), you may want to change the order of the photos. Just enter the numbers 1 through 6 (or however many photos you have) in the Order column: Make sure you click Save All Changes. On most computers, you can, instead of entering numbers, simply drag-and-drop images. WordPress will then generate the order numbers for you automatically. Then, you can review the Gallery Settings. There are a number of ways to use the gallery, but there is a single approach that I've found works for most people. You can experiment on your own with other settings and plugins, of course! I suggest you set Link thumbnails to to be Image File instead of Attachment Page. You can leave the other settings as they are for now. Once all of your settings are done, click on the Insert gallery button. This overlay box will disappear, and you'll see your post again. The page will have the gallery icon placeholder in the spot where you left the cursor, as seen in the following screenshot: If you're in the HTML view, you'll see the gallery shortcode in that spot: Note that because I'm uploading these photos while adding/editing this particular page, all of these photos will be "attached" to this page. That's how I know they'll be in the gallery on this page. Other photos that I've uploaded to other posts or pages will not be included in this gallery. Learning moreThe [gallery] shortcode is quite powerful! For example, you can actually give it a list of Media ID numbers—any Media item in your Media Library—to include, or you can tell it to just exclude certain items that are attached to this post or page. You can also control how the Thumbnail version of each image shows whether the medium or large. There is more! Take a look at the codex to get all of the parameters: http://codex.wordpress.org/Gallery_Shortcode Now, publish or save your page. When you view the page, there's a gallery of your images as follows: If you click on one of the images, you'll be linked to the larger version of the image. Now, this is not ideal for navigating through a gallery of images. Let's add a plugin that will streamline your gallery. Using a lightbox plugin A lightbox effect is when the existing page content fades a little and a new item appears on top of the existing page. You've seen this effect already in the WP Admin when you clicked on Add/Insert image. We can easily add the same effect to your galleries by adding a plugin. There are a number of lightbox plugins available, but the one I like these days uses jQuery Colorbox. Find this plugin, either through the WP Admin or in the Plugins Repository (http://wordpress.org/extend/plugins/jquery-colorbox/), and install it. Once you've activated the plugin, navigate to Settings | jQuery Colorbox: Use the Theme pull-down to choose the theme you want (the preview image will update to give you an idea of what it will look like); I've chosen Theme #4. Then you can choose to either Automate jQuery Colorbox for all images or Automate jQuery Colorbox for images in WordPress galleries. You can choose whether to automate for all images; I certainly suggest you automate for images in galleries. You can experiment with the other settings on this page (if you routinely upload very large images, you'll want to use the areas that let you set the maximum size of the colorbox and resize images automatically). You'll want to disable the warning (the very last check box on the page). Then, click on Save Changes. Now, when I go to my image gallery page and click on the first image, the colorbox is activated, and I can click Next and Back to navigate through the images: Summary In this article we saw how to add and manage built-in image galleries to display photos and other images. Further resources on this subject: WordPress 3 Complete [Book] WordPress 2.9 E-Commerce [Book] Getting Started with WordPress 3 [Article] How to Write a Widget in WordPress 3 [Article] Understanding jQuery and WordPress Together [Article] Tips and Tricks for Working with jQuery and WordPress [Article]
Read more
  • 0
  • 0
  • 3647
article-image-so-what-easeljs
Packt
18 Apr 2013
7 min read
Save for later

So, what is EaselJS?

Packt
18 Apr 2013
7 min read
(For more resources related to this topic, see here.) EaselJS is part of the CreateJS suite, a JavaScript library for building rich and interactive experiences, such as web applications and web-based games that run on desktop and mobile web browsers. The standard HTML5 canvas' syntax can be very hard for beginners, especially if you need to animate and draw many objects. EaselJS greatly simplifies application development in HTML5 canvas using a syntax and an architecture very similar to the ActionScript 3.0 language. As a result, Flash/Flex developers will immediately feel at home, but it's very easy to learn even if you've never opened Flash in your life. CreateJS is currently supported by Adobe, AOL, and Microsoft, and it's developed by Grant Skinner, an internationally recognized leader in the field of rich Internet application development. Thanks to EaselJS, you can easily manage many types of graphic elements (vector shapes, bitmap, spritesheets, texts, and HTML elements) and it also supports touch events, animations, and many other interesting features in order to quickly develop cross-platform HTML5 games and applications, providing a look and feel as well as a behavior very similar to native applications for iOS and Android. Following are the five reasons to choose EaselJS and HTML5 canvas to build your applications: Cross-platform — Using this technology will help you create HTML5 canvas applications that will be supported from: Desktop browsers such as Chrome, Safari, Firefox, Opera, and IE9+ iPhone, iPad, and iPod 4+ (iOS 3.2+) Android smartphones and tablets (OS 2.1+) BlackBerry browser (7.0 and 10.0+) Every HTML5 browser (go to http://caniuse.com/canvas for more information) The following screenshot shows how the same application can run on different devices and resolutions: Easy Integration — EaselJS applications run on browsers and finally can be seen by almost every desktop and mobile user without any plugin installed. The HTML5 canvas element behaves just like any other HTML element. It can overlap other elements or become part of an existing HTML page. So, your canvas application can fill the entire browser area or just a small part of an existing HTML page. You can create amazing image galleries for your sites, product configurators, microsites, games, and interactive banners, and replicate a lot of features that used to be created with Adobe Flash or Apache Flex. One source code — A single codebase can be used to create a responsive application that works on almost all devices and resolutions. If you've ever created a liquid or fluid layout using HTML, Flash, or Flex then you already know this concept. As shown in the previous screenshot, you can also adapt UI and change behaviors according to the size of the device being used. No creativity limits — As in Flash, you can now forget HTML DOM compatibility issues. When you display a graphic element using EaselJS, you can be sure it will be placed at the same position in every browser, desktop and mobile (except for texts because every browser uses a different font renderer, and there may be some minor differences between them and of course Internet Explorer 8 and lower versions that do not support HTML5 syntax). Furthermore the CreateJS suite includes a lot of additional tools helping developers and designers to create amazing stuff: TweenJS: An useful tween engine to create runtime animations PreloadJS: To load assets and create nice preloaders Zoë: To convert SWF (Adobe Flash native web format) into spritesheets and JSON for EaselJS SoundJS: A library to play sounds (this topic is not covered in this book) CreateJS Toolkit for Flash CS6: To export Flash timeline animations in an EaselJS-compatible format Freedom — Developers can now create and publish games and applications skipping the App Store submission process. Of course, the performance of HTML5 applications are not comparable to those achieved by the native applications but can still be an alternative solution to many needs. From a business perspective, it's a great opportunity because it is now possible to avoid following the Apple guidelines that usually don't allow publishing applications that are primarily marketing material or advertisements, duplicated applications or applications that are not very useful, or simply websites bundled as applications. Users can now have a cool touch experience directly while navigating through a website, avoiding having to download, install, and open a native application. Furthermore, developers can also use PhoneGap (http://www.phonegap.com) and many other technologies to convert their HTML applications in native applications for iOS, Android, Windows Phones, BlackBerry, Bada, or WebOS. After the previous introduction you will be guided through the process of downloading, installing and configuring EaselJS in your local machine (this part of the book is not copied in this article). The book continues with the traditional "Hello World" example, as shown in the next paragraph: Quick start — creating your first canvas application Now we'll see how to create our first HTML5 canvas application with EaselJS. Step 1 — creating the HTML template Take a look at the following code that represents the boilerplate we'll use: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>EaselJS Starter: Template Page</title> <script src = "lib/easeljs-0.6.0.min.js"></script> <script> // Your code here function init() { } </script> </head> <body onload="init();" style="background-color:# ccc "> <h1> EaselJS Starter: Template page </h1> <canvas id="mycanvas" width="960" height="450" style="background-color:#fff"></canvas> </body> </html> The following are the most important steps of the previous code: Define an HTML5 <canvas> object with a width of 960 pixels and a height of 450 pixels. This represents the drawing area of your EaselJS application. When the page is completely loaded, the onload event is fired and the init() function is called. The <script> block is the place where you have to add the code but you should always wait for the onload events before you do anything. Set the <body> and <canvas> background CSS styles. The result is a white container inside an HTML page, as shown in the following screenshot: Step 2 – creating a "Hello World" example Now replace the init() function with the following code: function init() { var canvas = document.getElementById("mycanvas"); var stage = new createjs.Stage(canvas); var text = new createjs.Text("Hello World!", "36px Arial", "#777"); stage.addChild(text); text.x = 360; text.y = 200; stage.update(); } Congrats! You have created your first canvas application! The following screenshot shows the output of the previous code, with a text field at the center of the canvas: The following are the most important steps of the previous code: Use the getElementById method to get a canvas reference. In order to use EaselJS, create a Stage property, passing the canvas reference as a parameter. Create a new Text property and add it to the stage. Assign values for the x and y coordinates in order to see the text at the center of the stage. Call the update() method on the stage to render it to the canvas. The Stage property represents the root level for the display list, which is the main container for all the other graphic elements. Now you only need to know that every graphic element must be added to the Stage property, and that every time you need to update your content you have to refresh the stage calling the update() method. Summary After the previous "Hello World" example the book will help you to learn how to use the most important EaselJS topics with practical examples, technical information, and a lot of tip and tricks, creating a small advertising interactive web application. By the end of book you will be able to draw graphic primitives and texts, load and preload images, handle mouse events, add animations and spritesheets, use TweenJS, PreloadJS, and Zoe and optimize your code for desktop and mobile devices. This article helped you to learn what EaselJS actually is, what you can do with it, and why it's so great. It also helped on hoe to create your first HTML5 canvas application "Hello World". Resources for Article : Further resources on this subject: HTML5: Developing Rich Media Applications using Canvas [Article] HTML5 Games Development: Using Local Storage to Store Game Data [Article] HTML5: Getting Started with Paths and Text [Article]
Read more
  • 0
  • 0
  • 3633

article-image-dynamic-menus-wordpress
Packt
07 Dec 2009
5 min read
Save for later

Dynamic Menus in WordPress

Packt
07 Dec 2009
5 min read
This is the nice thing about WordPress—it's all "dynamic". Once you install WordPress and design a great theme for it, anyone with the right level of administrative capability can log into the Administration Panel and add, edit, or delete content and menu items. But generally, when people ask for "dynamic menus", what they really want are those appearing and disappearing drop-down menus which, I believe, they like because it quickly gives a site a very "busy" feel. I must add my own disclaimer—I don't like dropdowns. Before you get on to my case, I will say it's not that they're "wrong" or "bad"; they just don't meet my own aesthetic standards and I personally find them non-user friendly. I'd prefer to see a menu system that, if subsections are required, displays them somewhere consistently on the page, either by having a vertical navigation expand to display subsections underneath, or showing additional subjections in a set location on the page if a horizontal menu is used. I like to be able to look around and say, "OK, I'm in the New Items | Cool Drink section and I can also check out Red Dinksand Retro Dinks within this section". Having to constantly go back up to the menu and drop-down the options to remind myself of what's available and what my next move might be, is annoying. Still haven't convinced you not to use drop-downs? OK, read on. Drop-down menus So you're going to use dropdowns. Again it's not "wrong"; however, I would strongly caution you to help your client take a look at their target users before implementing them. If there's a good chance that most users are going to use the latest browsers that support the current JavaScript, CSS, and Flash standards, and everyone has great mobility and is "mouse-ready", then there's really no problem in going for it. If it becomes apparent that any percentage of the site's target users will be using older browsers or have disabilities that prevent them from using a mouse and will limit them to tabbing through content, you must consider not using drop-down menus. I was especially negative about drop-down menus as, until recently, they required bulky JavaScripting or the use of Flash, which does not make clean, semantic, and SEO-friendly (or accessible) XHTML. Enter the Suckerfish method developed by Patrick Griffiths and Dan Webb. This method is wonderful because it takes valid, semantically accurate, unordered lists (WordPress' favorite!), and using almost pure CSS, creates dropdowns. The drop-down menus are not tab accessible, but they will simply display as a single, clear unordered list to older browsers that don't support the required CSS. IE6, as per usual, poses a problem or two for us, so there is some minimal DOM JavaScripting needed to compensate and achieve the correct effect in that browser. If you haven't heard of or worked with the Suckerfish method, I'm going to recommend you to go online (right now!) and read Dan and Patrick's article in detail (http://alistapart.com/articles/dropdowns). More recently, Patrick and Dan have revisited this method with "Son-of-a-Suckerfish", which offers multiple levels and an even further pared down DOM JavaScript. Check it out at http://www.htmldog.com/articles/suckerfish/dropdowns/. I also suggest you play around with the sample code provided in these articles so that you understand exactly how it works. Go on, and read it. When you get back, I'll review how to apply this method to your WordPress theme. DIY SuckerFish menus in WordPress All done? Great! As you can see, the essential part of this effect is getting your menu items to show up as unordered lists with sub unordered lists. Once you do that, the rest of the magic can be easily handled by finessing the CSS that Patrick and Dan suggest into your theme's CSS and placing the DOM script in your theme's header tag(s), in your header.php and/or index.php template files. Seriously, that's it! The really good news is that WordPress already outputs your content's pages and their subpages using unordered lists. Right-click on the page links in Firefox to View Selected Source and check that the DOM inspector shows us that the menu is, in fact, being displayed using an unordered list. Now you can go into your WordPress Administration panel and add as many pages and subpages as you'd like (Administration | Page | Add New). You'll use the Page Parent tab on the right to assign your subpages to their parent. If you installed the pageMash plugin, it's even easier! You can drag-and-drop your created pages into any configuration you'd like. Just be sure to hit the Update button when you're done. Once you've added subpages to a page, you'll be able to use the DOM Source of Selection viewer to see that your menu is displayed with unordered lists and sublists.
Read more
  • 0
  • 0
  • 3630
Modal Close icon
Modal Close icon