PHPFlickr and SSL/HTTPS – certificate issues on local Windows development server

We use PHPFlickr, which is a very handy PHP wrapper for the Flickr API, in CSlide. Recently, Flickr announced that they are making their API SSL only (on June 27th 2014), and PHPFlickr was updated accordingly. However, I could not get it to work on my local XAMPP development server, running on Windows 8. I initially assumed it was an issue with the changes to the PHPFlickr class (as it had previously worked fine), but, after much debugging, found that the problem was that CURL (which is used to make the requests to the API) could not verify Flickr’s SSL certificate. This is because CURLs default behaviour is to not trust any Certificate Authorities (CAs).

The solution is to give CURL a bundle of trusted CA certificates, which can be download from the CURL site. You can tell CURL about this bundle by setting the path to the CA bundle file in php.ini (and remembering to restart Apache afterwards). Adding this at the end of php.ini did the trick for me, but obviously change the path according to your own setup:

curl.cainfo=c:\xampp\php\cacert.pem

You can also point CURL at the CA bundle when setting up your curl connection, using curl_setopt to set CURLOPT_CAINFO to the full path to your CA bundle file. I didn’t want to do this with PHPFlickr, as it would involve changing the core PHPFlickr code, which would inevitably cause problems when that code gets updated:

curl_setopt($ch, CURLOPT_CAINFO, "c:\xampp\php\cacert.pem");

Note that one bad solution that was commonly suggested was to set CURLOPT_SSL_VERIFYPEER to false, which, as the name suggests, stop CURL from trying to verify the certificate. As rmckay says in the curl_setopt docs, this allows man in the middle attacks, so should be avoided!

Disabling touchscreen in Win 8.1

Having somehow managed to crack the screen on an Asus T100 on my way to work this morning, I was faced with the cracked area being interpreted as near-continuous touches, making the machine almost unusable. Thinking I’d turn off the touch on the screen, I found numerous posts suggesting that Human Interface Devices in Device Manager (in Control Panel) is the way to go and to look for and disable one of the USB Input Device entries. Having accidentally switched of the keyboard and trackpad several times (make sure you only try then one at a time!), I then spotted that my Asus has a device called HID-compliant touch screen. I disabled this and now have a fully working no touch notebook while I organise a repair.

AddThis _atssh div causing issues

I’m using AddThis in the new CSlide site, and it seems to add a div (id=”_atssh”), immediately after the link to the AddThis JavaScript file. This happens even if you have the JS file in the <head> section of the page, resulting in a div in the head, which clearly isn’t right. A quick search on the AddThis site suggests I am not the first to come across this issue, but it would appear the promised fix has not happened yet.

This can be fixed by putting the link to the JS file in the body (ideally at the end, for the sake of performance/progressive rendering*). However, having this div inserted at the end results in the size of the page height being increased by 1 pixel. Since, in my site anyway, it comes after a container with height set to 100%, it results in the page having a scrollbar when it shouldn’t need it.

Again, this can be fixed relatively easily, by adding the CSS below to your stylesheet. The offending div is invisible, so making it absolutely positioned (it used to already have position: absolute, but this seems to have changed (as at 6/01/2015)) and setting “top” to 0 just sticks it at the top of the page where it can’t interfere any more.

#_atssh {
    position: absolute;
    top: 0;
}

*I should mention that my JS was already at the end, and I only found out about the “div in the head” issue when looking into the page height issue.

CakePHP: Getting the full URL of the current page

In a CakePHP (2.3) project, I needed to echo the full (i.e. including scheme (http, https etc) and hostname) current URL in a view. I assumed this would be easy, and it is, but it wasn’t immediately obvious and took a bit of searching. The following examples assume we are on localhost, the project is called ‘project’ and the current page is /controller/action/1, so we would expect the full URL to be: http://localhost/project/controller/action/1.

The way to do it:

All we need to do is to echo a null url, with the ‘full’ option set to true, which means the scheme, hostname and project path are included. As well as null, any “falsey” value seems to work, e.g. false, 0, “”, and even array().

echo $this->Html->url(null, true);

Produces: http://localhost/project/controller/action/1

The ways not to do it:

echo $this->here;

Produces: /project/controller/action/1. This is missing the scheme and hostname.

echo $this->Html->url();

Produces: /project/controller/action/1. Again, this is missing the scheme and hostname.

echo $this->Html->url( $this->here, true );

Produces: http://localhost/project/project/controller/action/1. Here ‘project’ appears twice, once from $this->here and once as we have set the ‘full’ option to true.

Google Maps need resizing after being “shown” with JavaScript/JQuery

If you have a Google Map on a page that, for example, starts off hidden and is shown using JavaScript (inc. JQuery or another library), it seems to only load the top left map tile. In order to fix this, you need to resize the map after showing it, using the following line:

google.maps.event.trigger(map, 'resize');    //map is your google.maps.Map object

This is mentioned in the resize event in the Map class documentation.

XML views in CakePHP 2.x

In a CakePHP project, I needed to extract an XML string from the database and display it as an XML page. The docs suggested this would be easy with the XmlView, but I struggled to get it to work. However,  after a fair amount of fiddling around, I found an easy way to display an XML view.

Here’s what I had to do:

  • Enable parsing of .xml extensions in routes.php:
    Router::parseExtensions('xml');
  • Set the viewClass to Xml in the controller:
    $this->viewClass = 'Xml';
  • Create a simple xml view in Views/YourController/xml/xml.ctp, and echo $xml in this view:
    <?php echo $xml; ?>
  • Back in the controller, set the XML string to the view:
    $this->set('xml', $xmlString);
  • Finally, render using the xml view you have just created (this must be after the ‘set’ line above):
    $this->render('xml');

Unfortunately, this does not take advantage of the _serialize key, which would mean you don’t need to create a view file at all, but I just couldn’t get this to work with an XML string, despite trying various combinations of CakePHP’s Xml Class functions.

Authenticate (SSO) Vanilla users with a CakePHP 2 application using Vanilla’s jsConnect Plugin

Using your existing CakePHP website to authenticate users into a Vanilla forum (so they don’t have to setup another account) is fairly simple…once you know how:

In your Cake site:

  1. Download the PHP jsConnect client library as described here
  2. Create a ‘vanilla’ directory in app/Vendor and copy into it functions.jsconnect.php from the download in 1.
  3. In UsersController.php add:
    App::import(
     'Vendor',
     'FunctionsJsconnect',
     array('file' => 'vanilla' . DS . 'functions.jsconnect.php')
    );
  4. Paste index.php from the download in 1 into View/Users and rename it as vanilla_authenticate.ctp. Edit so that it contains only:
    // 1. Get your client ID and secret here. These must match those in your jsConnect settings.
    $clientID = "your_client_id_from_js_connect_plugin_settings_page";
    $secret = "your_secret_from_js_connect_plugin_settings_page";
    // 3. Fill in the user information in a way that Vanilla can understand.
    // CHANGE THESE FOUR LINES.
    $user['uniqueid'] = $user_data['your_cake_id_field'];
    $user['name'] = $user_data['your_cake_username_field'];
    $user['email'] = $user_data['your_cake_email_field'];
    $user['photourl'] = $user_data['your_cake_avatar_field'];
    // 4. Generate the jsConnect string.
    // This should be true unless you are testing. 
    // You can also use a hash name like md5, sha1 etc which must be the name as the connection settings in Vanilla.
    $secure = true; 
    WriteJsConnect($user, $_GET, $clientID, $secret, $secure);
  5. In UsersController.php, add a vanilla_authenticate action:
    public function vanilla_authenticate(){
        if ($this->Auth->loggedIn()){
             //user is logged in so pass necessary data through to view
             $this->layout = 'ajax';
             $user_data = $this->Auth->user();
             $this->set('user_data', $user_data);
             }
         }
  6. Again in UsersController.php, modify your login action:
    public function login() {
        if ($this->request->is('post')) {
            ...
            //Do your normal login business which includes the line
            $this->redirect($this->Auth->redirectUrl()); //after successful login
            ...
            }
        //add following to handle get request from jsConnect
        else{
            if(isset($this->request->query['source'])&&$this->request->query['source']=="vanilla"){
    	    //we need to redirect to vanilla after login so change redirectURL
    	    $this->Auth->redirectUrl('http://www.yourvanillasite.com/index.php?p=/entry/jsconnect&client_id=your_client_id_from_js_connect_plugin_settings_page&Target=http://www.yourvanillasite.com'); //big thanks to @hgtonight for this snippet
    	    }
    	}
        }

In your Vanilla forum:

  1. Download and enable the jsConnect plugin (at the moment, the advice is to use 1.03 as 1.4.1 is still buggy, reporting a ‘regex’ error);
  2. In the jsConnect plugin settings:
    • autogenerate (using the button at the bottom) a ClientID and Secret;
    • the Site Name: Your Cake Site – this will appear as ‘Sign in with {site name}’ when users go to the Vanilla forum
    • Authenticate URL:  http://www.yourcakesite.com/users/vanilla_authenticate
    • Sign In URL: http://www.yourcakesite.com/users/login?source=vanilla
    • Register URL: http://www.yourcakesite.com/users/register

Well, that’s what worked for me anyway….

Vanilla Community’s x00 tells me that you can avoid the complicated redirect in Cake Step 6 above (and replace with:

$this->Auth->redirectUrl('www.yourvanillasite.com');

if you use the jsConnect AutoSignIn Plugin…but I haven’t tried this yet.

Good luck.

Complete Idiots Guide to using Composer with CakePHP

I recently wanted to use Miles Johnson’s Forum Plugin for CakePHP in a project, and when I came to install it, saw that he recommends (insists on, in fact) using Composer to do this, to take care of ensuring all the dependencies are installed. Never having used Composer before, I took a look at his post on using Composer with CakePHP, which probably counts as an idiots guide for most people, but still wasn’t basic enough for me. Therefore, I thought I would write a complete idiots guide!

The steps I had to go through to get it working were as follows:

  • Install Composer – I am on Windows, so just downloaded and ran the Windows installer, which seemed to work fine
  • Ensure that the openssl extension is enabled in php.ini, i.e. uncomment this line: extension=php_openssl.dll
  • Create a composer.json file in your app directory and add the dependencies, e.g. for the Forum plugin:
    {
       "config": {
          "vendor-dir": "Vendor"
       },
       "require": {
          "mjohnson/forum": "5.*"
       }
    }
  • Run composer install in the app directory. This should install all of the dependencies (in the Vendor or Plugin directories of your app, as appropriate) and create an autoload.php file in the Vendor directory
  • Add the following at the start of your Config/core.php file to make use of Composer’s autoloading capabilities:
    require_once dirname(__DIR__) . '/Vendor/autoload.php';

That should deal with all of the dependencies, in this case for the Forum Plugin. It is still necessary to load the Plugins in the Config/bootstrap.php file. For the Forum Plugin, this is does as follows:

CakePlugin::load('Utility', array('bootstrap' => true, 'routes' => true));
CakePlugin::load('Admin', array('bootstrap' => true, 'routes' => true));
CakePlugin::load('Forum', array('bootstrap' => true, 'routes' => true));

IOS 7 status bar with fixed footer navbar in Cordova/JQuery Mobile

To avoid your content scrolling underneath the new IOS 7 status bar, you have a few choices but in most situations, org.apache.cordova.statusbar is the best solution.

From the CLI:

$ cordova plugin add org.apache.cordova.statusbar

And you can then turn the transparent IOS7 status bar into one which behaves as it does in IOS6 with the following in your config.xml:

<preference name="StatusBarOverlaysWebView" value="false" />
<preference name="StatusBarBackgroundColor" value="#000000" />

However, if you have a fixed navbar in the footer such as this to approximate the look of a tab bar in the new IOS7  Human Interface Guidelines:

<div data-role="footer" data-id="allot_footer" data-position="fixed">
    <div data-role="navbar" data-iconpos="bottom">
        <ul>
            <li><a href="#browse" data-id="footer_browse" data-theme="d" data-icon="grid">Browse</a></li>
            <li><a href="#find" data-id="footer_find" data-theme="d" data-icon="search">Find</a></li>
        </ul>
    </div>
</div>

then you’ll find that the statusbar plugin pushes the navbar down off the bottom of the page by the height of the status bar. So, for a fixed bottom navbar, the solution I’ve adopted inn the end is to forget the statusbar plugin and to allow the status bar to overlay my content. However, to create a bit of space at the top of the page for it to sit, I used this Gist from Shazron to arrive at:

function onDeviceReady() {
    if (parseFloat(window.device.version) >= 7.0) {
        $("div.ui-header").css("padding-top", "20px"); 
        }
    ...
    }

Note that you’ll need to install org.apache.cordova.device using the CLI:

$ cordova plugin add org.apache.cordova.device

It now looks fine on startup but if you need to scroll your content, it’ll still get overlayed by the IOS7 status bar so make your header fixed too:

<div data-role="header" data-id="allot_header" data-position="fixed" data-theme="d">
    <h1>Browse</h1>
</div>

Hoping that IOS7 issues will all become a distant memory very soon, but this is what works for me at the moment.