PHP (20)


concrete5 – Working with custom Permission Keys

When you build an add-on with concrete5, you’ll sometimes want to hide a few things depending on the users or group. You might be tempted to hard-code a check to a specific group, but there’s a much more elegant way to handle this. It also gives you a lot more power to control who’s allowed to do something, just in case your customer changes.

In this tutorial, we’re building a new package called “codeblog_task_permissions” which you have to put in your “packages” directory. Within the new package directory, create a new file called “controller.php” and put the following content in it, we’ll have a closer look at what it does afterwards:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php
 
class TaskPermissionsPackage extends Package {
 
    protected $pkgHandle = 'task_permissions';
    protected $appVersionRequired = '5.6.3';
    protected $pkgVersion = '1.0';
 
    public function getPackageDescription() {
        return t("Installs the Task Permission demo package.");
    }
 
    public function getPackageName() {
        return t("Task Permissions");
    }
 
    public function install() {
        $pkg = parent::install();
        $this->installTaskPermissions($pkg);
    }
 
    /**
     * This method installs our permission keys
     * 
     * @param Package $pkg
     */
    protected function installTaskPermissions($pkg) {
        // add a new permission key to handle shutdons
        $pkShutdownHandle = 'shutdown_planet';
        if (!is_object(PermissionKey::getByHandle($pkShutdownHandle))) {
            $pkShutdown = PermissionKey::add('admin', $pkShutdownHandle, t('Shutdown the planet'), t('Permission to shutdown the planet'), '', '', $pkg);
 
            // assign administrators the right to handle our planet
            $group = Group::getByID(ADMIN_GROUP_ID);
            $adminGroupEntity = GroupPermissionAccessEntity::getOrCreate($group);
 
            $pa = PermissionAccess::create($pkShutdown);
            $pa->addListItem($adminGroupEntity);
            $pt = $pkShutdown->getPermissionAssignmentObject();
            $pt->assignPermissionAccess($pa);
        }
 
        // install a second permission key to control the weather
        $pkWeatherHandle = 'make_weather_nice';
        if (!is_object(PermissionKey::getByHandle($pkWeatherHandle))) {
            $pkWeather = PermissionKey::add('admin', $pkWeatherHandle, t('Remote Weather Control'), t('Access to the remote weather control system'), '', '', $pkg);
        }
    }
 
}

Let’s have a look at the simpler example. In line 44 we’re setting the handle of the permission key, it’s what we need to work with it when we run our check in the code. In the next line, we check if the permission key is already installed, if it isn’t, we add it. These are the parameters you can use:

public static function add(
        $pkCategoryHandle, 
        $pkHandle, 
        $pkName, 
        $pkDescription, 
        $pkCanTriggerWorkflow, 
        $pkHasCustomClass, 
        $pkg = false
)

That’s all we need to add a custom attribute key. If you install the package and navigate to “/dashboard/system/permissions/tasks/” in your dashboard, you’ll see your custom permission keys at the end of the screen.

permissionkeys

As you can see, our new permission key doesn’t have any groups assigned to it. You can either do that manually, or by code. In the example above, the first permission key shows you how to do that. Just have a look at the lines 33 to 40. We get an instance of our administrators group and then pass it along a permission access object which we can assign to our permission key.

Now that we have created our permission keys, we want to use them. This part is even easier, all you have to do is to create a PermissionKey object and run the “can()” method:

1
2
3
4
5
6
$pk = PermissionKey::getByHandle('shutdown_planet');
if ($pk->can()) {
    echo t('Yes you are allowed to shutdown the planet');
} else {
    echo t('We are sorry but you have no permissions to shutdown the planet');
}

You can find the complete example on github, https://github.com/Remo/codeblog/tree/master/codeblog_task_permissions. If you use the example and want to check the permissions, point your browser to http://1/index.php/tools/packages/task_permissions/check_permissions and you’ll see weather you have the permission to the keys or not.




Book Review – Learning FuelPHP for Effective PHP Development

I’ve been playing around with FuelPHP for a while and despite the fact that I haven’t used it in production, it has been on my watch list ever since I first saw it. When I saw the new book about FuelPHP by Ross Tweedie, I was eager to read it – here’s my feedback about. If you just want to buy the book, you can get it at Amazon or directly from Packt Publishing.

About the book

The book has about 80 pages of actual content and covers these topics:

  • Chapter 1 – A few words about the history, some ideas behind FuelPHP and a good bit of theory.
  • Chapter 2 – Installation using the console script and a few words about alternative methods like GIT.
  • Chapter 3 – You’ll find information about the architecture in this chapter, (H)MVC, Configurations etc.
  • Chapter 4 – Shows you how to create models, controllers to build a simple blog application.
  • Chapter 5 – A list of packages you can find in the internet as well as a quick introduction showing you how to create your own package.
  • Chapter 6 – A few advanced topics like unit testing, routing concepts, custom oil (the FuelPHP console tool) tasks and profiling.
  • Chapter 7 – briefly covers the FuelPHP community and where to find more information about FuelPHP if you need it.

Targeted Audience

Without having read who the author actually targets, to me it feels like you’ll have to know quite a bit of PHP and some of the tools you use when writing software. At the beginning, you’ll have a nice overview where you can get a good understanding for FuelPHP and why yet another framework does make sense. The author also starts to use some words like closures, singletons, multitons pretty quickly. There are a few words explaining it, but I don’t feel I’d have actually understood it, if I haven’t worked with these things before. Not every person learns the same way, but I’d have liked to see a few links where I could actually see some example code of a multiton pattern.

Not the fault of the author, but software changes and usually much quicker than paper does. The book seems to have used version 1.6 as well as 1.7 when writing his book. I started straight with the current version which is currently 1.7.1. The good news, it will work just fine, even if the book is focused on 1.6. Not related to the book but more about FuelPHP, version 2.0 has been announced a while ago. The author also covers some changes we’ll see and that’s a bit of a downside to me. I do like everything that will change, but it makes me want to skip the 1.x version.

The bad sides

The author seems to be a skilled developer and likes to write software. I believe that, because I see some similarities with my own behaviour. The book covers quite a few things, things like GIT which are great (imho), but the explanation will hardly be good enough for someone who hasn’t worked with it before. I’d have recommended to keep that part in a blog and simply link to it. As soon as you run into an issue with GIT, you’re on your own. Although, I tried to go through the book without setting up a repository and it worked fine.

There are other situations, for example in the installation where the required PHP modules are described, but not with a name you can use to find the actual PHP extension you’ll need. Luckily that shouldn’t be an issue since FuelPHP doesn’t require much.

While FuelPHP works on different webservers like Apache, Nginx, IIS and probably a few more, most of the book is focused on Apache and *nix. There are a few shell commands that you won’t be able to run on a Windows computer, at least if you don’t want to play around with Cygwin or something similar.

Here’s a list which I think should be improved if there’s going to a be a second edition:

  • What the heck is a “Temporal ORM”? I do know quite a bit about SQL, ORM but was surprised to see a term right at the beginning of a book which is completely new to me. Luckily for me, Google also has only 36 results about this expresison
  • There are some magic commands in the book. While I understand their benefit, I’d recommend to leave them out and focus on the actual FuelPHP part. An information box with a sentence or two and a link would have been sufficient.
  • Probably something the publisher should improve. Just like my books, the code formatting could be better – I’ve seen worse but perfectly indented code is nice! I’d also recommend to highlight the line that actually matters.
  • I was sometimes confused about instructions, they were in the middle of a step by step procedure but sounded quite theoretical.
  • Make sure things are complete, when writing that you simply have to add a package to composer.json to install a new module, that’s incorrect, you’ll have to run composer update too. These things are small, but details matter, especially to beginners

You’ll find a few more situations, but after all I like the book and don’t want to give you an impression that’s worse than I think it should be.

The good parts

I like the started, the introduction about FuelPHP. I often ask myself: Do we really need this? Whether it is a PHP framework, a new Linux desktop. You’ll get a good feeling about what FuelPHP is about.

The demo application is perfect, it shows you how to create models in no time, how to run migration scripts to manage the creation but also the change of the database structure. It quickly covers the controller part, a few words about models and their relationships and bit about the output. You’ll also learn how to create a module and a package, their difference is well explained but will change in v2.0 as far as I know.

Should I read this book?

It depends on your background – I wouldn’t recommend it if you’re a PHP programmer who hasn’t worked with namespaces, databases before. But please keep in mind that this is a short book of only about 80 pages content. FuelPHP might have deserved a bit more but the shortness was nice – at least for someone like me who has worked with all kinds of frameworks. I’d definitely recommend to book if you worked with other frameworks like CodeIgniter, Yii, Zend .. before and now want to have a look at FuelPHP. You’ll get a good impression about its possibilities!




concrete5 – programmatically creating a page alias

in concrete5 you can create an alias if you want a page to appear at two different locations. If you look at the following site structure, you can see that we have two categories and two different services we’d like to offer.

alias_1

For whatever reason, we’d like to make sure that our main service shows up beneath both categories. It’s physical location is right under the first category but how do we add an alias underneath the second category? In the UI this is super easy, just drag the service to the second category and release the mouse button – a dialog will show up where you can select that you want to create an alias and that’s it! But how do we the same using code? It’s also pretty simple:

1
2
3
4
<?php
$pageToAlias = Page::getByPath('/category-1/service');
$parentOfAlias = Page::getByPath('/category-2');
$newAlias = $pageToAlias->addCollectionAlias($parentOfAlias);

I’ve used Page::getByPath to get both page objects but you can of course use Page::getByID or anything else that returns a valid page object. As soon as you’ve run this code, you’ll have an alias in your sitemap like that:

alias_2




concrete5 – smart attribute/picture fetching through page hierarchy

In this article I’m going to explain a nice code that works with attribute. It’s something quite a few people are using but it never crossed my mind to write something about it until my friends from 100pro asked for it.

Let’s start with a quick look at the problem we’re going to solve. Our page layout is pretty simple, two columns, some content and in our case the most important part: A picture in the head of every page:

attribute_recursion_header_pic

Assuming we’ve got the following structure in our sitemap:

attribute_recursion_sitemap

As you can see, this company has two slightly different offers on their site. Due to that, the boss wants to have different pictures that match the two topics. Easy, you might say, just add a block in the header of each page – until you notice that there are quite a few pictures & videos and thus lots of pages. Still easy you might say, why not create two page types and use the page type defaults to manage these pictures? Well, you could do that but using a page type for that feels wrong and sometimes it’s not that clear that you need two different pictures.

Silly story short: We want to specify a picture for “Business 2 Business” which is used on that page as well as all its children and we also want to specify a picture for “Funny Things” and its children.

Use an attribute from parent pages

We’re going to use an attribute to assign a picture to these two pages. If you’re logged in, open this page on your site: /dashboard/pages/attributes/. At the bottom, select “Image/File” and hit “Add”. Enter “header_pic” for handle and “Header Picture” for name.

Next, go back to the sitemap /dashboard/sitemap/full/ and click on “Business 2 Business” and then “Properties”. If you switch to “Custom Attributes” and scroll to the bottom, you’ll find our new attribute. Click on it and select a picture of your choice. Save everything and do the same for the second page “Funny Things”. Now that we’ve assigned the two pictures to our pages, it’s time to do the coding.

Fetch attribute from parent pages

This step might look slightly different depending on your theme but should be farily easy to adapt with a basic understand of HTML. The header area you want to replace with the picture selected in the attribute is mostly likely found in elements/header.php of your theme. In my case, the part we have to modify looks like this:

1
2
3
4
5
6
7
8
9
10
<div id="header-area">
   <div class="divider"></div>
   <div id="header-area-inside">
   <?php 			
   $ah = new Area('Header');
   $ah->display($c);			
   ?>	
   </div>				
   <div class="divider"></div>
</div>

Looks have a look at the comment code that replaces the code above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<div id="header-area">
    <div class="divider"></div>
    <div id="header-area-inside">
        <?php
        $nh = Loader::helper('navigation');
        $ih = Loader::helper('image');
 
        $c = Page::getCurrentPage();
 
        // get list parent pages of current page
        $collectionTrail = $nh->getTrailToCollection($c);
 
        // add current page to array
        array_unshift($collectionTrail, $c);
 
        // loop through all the pages back to the home page
        foreach ($collectionTrail as $page) {
            // get attribute from page
            $picture = $page->getAttribute('header_pic');
 
            // check if attribute on page exists
            if ($picture instanceof File) {
                // make sure the picture isn't bigger than 800x300, cameras
                // these days take huge pictures
                $thumbnail = $ih->getThumbnail($picture, 800, 300, true);
 
                // print thumbnail
                echo "<img src=\"{$thumbnail->src}\" alt=\"\"/>";
 
                // stop searching through more pages
                break;
            }
        }
        ?>	
    </div>				
    <div class="divider"></div>
</div>

This code uses the navigation helper to get a list of all the parent pages and then loops through all of them until a page is found where the attribute with the handle “header_pic” has a valid file. In this case, we replaced the existing area but you can of course also keep it and add the code shown above as an extension.

The method “getTrailToCollection” is usually used to build a breadcrumb like navigation. There’s an autonav template called “breadcrumb” that does that as well but if you wonder how to create a breadcrumb navigation with code, here’s an example:

1
2
3
4
5
6
$nh = Loader::helper('navigation');
$c = Page::getCurrentPage();
$collectionTrail = array_reverse($nh->getTrailToCollection($c));
foreach ($collectionTrail as $page) {
    echo "<a href=\"{$nh->getLinkToCollection($page)}\">{$page->getCollectionName()}</a> ";
}

I hope you were able to get nice pictures with little effort into your site! If not, post a comment and I’ll do my best to get it fixed!




Getting started with Google App Engine for PHP and concrete5

You might have read or heard that Google added PHP as a supported language to their cloud hosting platform App Engine. The official documentation is available here and gives you a lot of information to get started:
https://developers.google.com/appengine/docs/php/gettingstarted/.

If you’re a returning visitors to this blog, you’ll probably have noticed that I often work with a CMS called concrete5. It’s a neat CMS to work with for end users but also very powerful to extend for developers.

No surprise, I wanted to see if I could get concrete5 running on GAE for PHP. The documentation has clear instructions that help you to get a test environment ready on your local computer. You’ll need to install Python, PHP as well as the Google App Engine SDK for PHP, more about that here: https://developers.google.com/appengine/docs/php/gettingstarted/installing.

The Hello world tutorial explains almost everything you need to know to get concrete5 running, I’d recommend that you follow it too, it only takes you a few minutes to install everything and get the famous “hello world” output on your screen https://developers.google.com/appengine/docs/php/gettingstarted/helloworld. As you can see in this tutorial, Google doesn’t use a htaccess file but rather a file called app.yaml. This is where you specify your applications settings as well as redirect rules. The most important part is this:

- url: /.*
  script: index.php

This will tell App Engine to redirect all requests to index.php which is exactly what concrete5 does too, but normally using this:

RewriteRule . index.php [L]

However, there’s one more thing we have to take care of, there are static files that won’t be served correctly at the moment. Let’s have a look at the original .htaccess file first:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}/index.html !-f
RewriteCond %{REQUEST_FILENAME}/index.php !-f
RewriteRule . index.php [L]
</IfModule>

Let’s just focus on the first RewriteCond, this will make sure that the RewriteRule statement is only executed if the requested file doesn’t exist. If /concrete/themes/default/main.css is requested, Apache will know that it exists as a real file and stops the rewrites process, but if /about is requested, Apache will forward that request to index.php. I couldn’t find the exact same functionality but with an additional handler, I was able to see resources like the pictures just fine. Here’s the complete content of app.yaml:

application: concrete5
version: 1
runtime: php
api_version: 1

handlers:
- url: /(.*\.(ico$|jpg$|png$|gif$|css$|js$))
  static_files: \1
  application_readable: true
- url: /.*
  script: index.php

When working on your local computer, you can easily use your existing MySQL installation but if you want to deploy your application to the cloud, things work a bit different. Unfortunately I wasn’t aware of that, I thought that if I only use very little resources, App Engine would be free. From what I can tell, you can run a PHP application for free but as soon as you want to store something in a database, you have to pay. There are two different options, first Google Cloud Storage which is a simple database without a lot of functionality, if you want to port an existing SQL based application, you might want to look at Google Cloud SQL which is a MySQL database running in the cloud. More about that here https://cloud.google.com/products/cloud-sql. Here are a few technical things that you might want to check out https://developers.google.com/appengine/docs/php/cloud-sql/developers-guide.

Since I wanted to move my concrete5 site concrete5.ch to Google App Engine, the fact that storage isn’t free, is a killer argument at the moment. I’d very much like to finish the experiment but I’ll wait till I have a project where I actually make some money before I spend money.

However, I’m pretty confident that I could get concrete5 running on App Engine.




concrete5 database list and pagination

If you ever worked with tables in a web application, you’ll probably have had to build some kind of pagination. If you only have very few entries, that’s not necessary but if your database grows, you’ll probably need to display the data in pages of 20 items. In this tutorial, which is aimed at developers knowing concrete5, I’ll show you the use of a helper in concrete5 that does a nice job and helps you a lot in a way you’ll always add a pagination, even if you think you’ll only have 10 entries in the table.

This article contains quite a bit of here, let’s start with an overview of all the elements we’re going to build first:

  • A new package which holds all elements together
  • A “single page” to show you the use of our database item list class
  • Two models, one to manage our data and one which builds a list of it

More about the break!




Share content across site in concrete5

If you worked with concrete5 before you probably know that there’s an in-site editing system where you can place a different kinds of blocks in areas. Usually a page has several areas and your site probably has several pages. This concept might have been a bit uncommon but once you’re used to it, it’s rather powerful and easy to understand as it allows you to edit your site in a layout more or less identical to the layout the site has to visitors.

Placing content in a block works fine but if you wanted to have the same content on several pages it’s going to be a bit tricky. Assume you want to put a certain content in the footer of every page. There used to be a feature called “Scrapbook” which has been replaced by “Stacks”. You can find more information about this on the following page: http://www.concrete5.org/documentation/how-tos/developers/concrete5-5.5-stacks-vs.-scrapbooks/. This is one option you can use, but there’s another one where you can stay on the page you’re editing while amending sitewide content (when editing a “Stack” you’re being redirected to a dashboard page).




Improved PHP error reporting

Unfortunately I’m still not able to write thousands of lines without making a mistake, I’m still trying of course! In most situations you’ll get enough information from PHP to fix the problem quickly but sometimes you have to navigate through a bunch of files until you’ve found the origin of the problem.

This can be annoying and time consuming.

Luckily Joseph Lenton wrote a rather nice tool which shows you more information and it’s also very easy to use. There’s no PHP module involved, no installed required like XDebug.

Get the source of php_error.php from this page: https://github.com/JosephLenton/PHP-Error.
After that all you need to do is to include this at the top of the first file you’re working with:

require( 'php_error.php' );
\php_error\reportErrors();

After that, you’ll see a nice output like this if you make a mistake:

It might happen that you’re getting a bunch of stuff which you don’t care about. That’s probably because you have some warning or notices about deprecated functions. You can either fix them or if you’re lazy, change the error_reporting like this:

require( 'php_error.php' );
 
$options = array(
       'snippet_num_lines' => 10,
       'error_reporting_off' => 0,
       'error_reporting_on' => E_ERROR | E_WARNING | E_PARSE
);
\php_error\reportErrors( $options );

You can find more options here: https://github.com/JosephLenton/PHP-Error/wiki/Options.

Have fun!




concrete5 – Custom Toolbar Button

with concrete5 you usually put your add-on code in a package and don’t touch the core. This concept allows you to update the CMS core without overriding anything you’ve built on your own. In order to interact with the CMS you can use several functions helping you to integrate your add-on into the CMS interface.

In this short article we’re going to look at a simple example which will place a button next to the edit button and shows a dialog with a quote. Nothing fancy, just an example aimed at developers who want to use this feature in their add-ons.

This is how your button is going to look like:




concrete5 SooperFish drop down navigation

Creating a drop down navigation is an old technique by now but it’s still used in a lot of cases to hide parts of a navigation. In addition to the plain CSS menu I wrote more than 2 years ago, I decided to write a new tutorial which uses JavaScript as well. You might ask why: Avoiding JavaScript is nice but creating something as complex as a drop down navigation without any JavaScript leads to a few ugly work arounds. You’ll also have some difficulties to add a fade out and fade in effect unless you’re using CSS3 which isn’t well supported yet.

But at the end it’s up to you, both solutions can work just fine!

Using SooperFish is also a bit easier for us, you’ll see at the end of the tutorial how little code you needed.
At the end your navigation can look like this: