Baking cakes with CakePHP.

  • Rails 3 and Passenger

    This weekend the RailsBridge people have organized a bugmash with the motto “Do One Thing for Rails 3″, and so I took the opportunity to experiment a bit with the coming Rails 3.

    After following the instructions for creating a new Rails 3 app (using the –database=mysql parameter for the “rails” command) I noticed that the application tried to access the (not existing) production database. That was a bit strange, because I expected it to access the development database as I set the RailsEnv option of Passenger accordingly:

    # for example in /etc/httpd/conf/httpd.conf
    RailsEnv development
    

    The reason this doesn’t work is because the generated application contains a “config.ru” file. In this case, Passenger treats the application as a Rack application and not as a Rails application. And so the RailsEnv setting is ignored as it is Rails-specific…

    There are two ways to run the application in development mode: you can either remove the “config.ru” file (and keep the RailsEnv setting) or you can set the RackEnv option:

    # for example in /etc/httpd/conf/httpd.conf
    RackEnv development
    

    Have fun with Rails 3 :)

    8 comments

  • Accepting the Google OpenID with PHP OpenID

    If you are using the PHP OpenID library (which is also used by my OpenID component for CakePHP), it is possible that you get an “Invalid OpenID” error when you try to login with the Google OpenID (https://www.google.com/accounts/o8/id), or any other OpenID that uses “https”.

    In this case, the following steps might help to fix this issue:

    • Ensure you have Curl and OpenSSL installed
    • Enable the Curl and OpenSSL extensions in your php.ini (on Archlinux this file is found in /etc/php/):
      extension=curl.so
      extension=openssl.so
      
    • Restart your web server

    Now the error message should disappear and you should be able to log in with the Google OpenID.

    —-

    That’s it for 2009. It was a rather lazy year on this blog from my side (I didn’t even manage to do a redesign…), and so I hope I will be a bit less lazy with writing on this blog in 2010.

    Anyway, thank you for reading this blog, and for all your comments and emails. A Happy New Year everyone & cu in 2010 :)

    4 comments

  • Attribute Exchange support for the OpenID component

    The OpenID Attribute Exchange specification (or AX for short) has been around for quite a while, though I ignored it so far because at the time it was introduced (almost) no OpenID provider supported it. However, after Yahoo! announced they support Attribute Exchange, and someone recently mentioned it in a mail, it was time for me to have a look at it.

    AX is in principle the “big brother” of the Simple Registration Extension (or SReg for short). Whereas SReg only allows you to retrieve nine commonly requested pieces of information, AX allows you to retrieve any identity information. And theoretically it also allows you to store/update your identity information at your OpenID provider. But it seems like no OpenID provider supports this feature…

    Let’s have a look at an example.

    First the login method:

    // app/controllers/users_controller.php
    class UsersController extends AppController {
        public $components = array('Openid', 'RequestHandler');
    
        public function login() {
            $realm = 'http://'.$_SERVER['SERVER_NAME'];
            $returnTo = $realm . '/users/login';
    
            if ($this->RequestHandler->isPost()) {
                $this->makeOpenIDRequest($this->data['User']['openid_identifier'], $returnTo, $realm);
            } elseif ($this->Openid->isOpenIDResponse()) {
                $this->handleOpenIDResponse($returnTo);
            }
        }
    }
    

    The next step is to implement the makeOpenIDRequest() method. For each attribute we want to retrieve, we have to create an Auth_OpenID_AX_AttrInfo object with the respective attribute type. A list of possible types is available on http://www.axschema.org/types/. Though there are many types defined, OpenID providers usually only support a small subset of those types.

    The “1″ we pass to the make() method specifies the number of values we want for this type. In this example it doesn’t make much sense to specify a value other than “1″, but for other types it is theoretically possible to have multiple values (for example you could have defined multiple email addresses). It is an optional parameter and by default it is “1″.

    The last parameter specifies whether the value of the attribute is required for our application. This is simply a hint for the OpenID provider so it could display this attribute differently, but it doesn’t guarantee a value is returned. By default this parameter is “false”.

    private function makeOpenIDRequest($openid, $returnTo, $realm) {
        $attributes[] = Auth_OpenID_AX_AttrInfo::make('http://axschema.org/namePerson', 1, true);
        $this->Openid->authenticate($openid, $returnTo, $realm, array('ax' => $attributes));
    }
    

    Finally, we have to implement the handleOpenIDResponse() method. As we expect only one value for the attribute we specified, we can use either get() or getSingle() to retrieve its value. getSingle() returns the value whereas get() returns an array.

    private function handleOpenIDResponse($returnTo) {
        $response = $this->Openid->getResponse($returnTo);
    
        if ($response->status == Auth_OpenID_SUCCESS) {
            $axResponse = Auth_OpenID_AX_FetchResponse::fromSuccessResponse($response);
    
            if ($axResponse) {
                debug($axResponse->get('http://axschema.org/namePerson'));
                debug($axResponse->getSingle('http://axschema.org/namePerson'));
            }
        }
    }
    

    That’s it.

    You can get the new version of the OpenID component from GitHub. If you use SReg in your code and you want to update to this version, please make sure to adapt your code in the following way:

    // old
    $this->Openid->authenticate($openid, $returnTo, $realm, array('email'), array('nickname'));
    
    // new
    $this->Openid->authenticate($openid, $returnTo, $realm, array('sreg_required' => array('email'), 'sreg_optional' => array('nickname')));
    

    Feedback is welcome :)

    6 comments

  • Render partial from an atom builder view

    Thanks to the built-in AtomFeedHelper it is quite easy to generate an atom feed with Rails.

    Here an example from the API:

    # app/views/posts/index.atom.builder
    atom_feed do |feed|
      feed.title("My great blog!")
      feed.updated(@posts.first.created_at)
    
      @posts.each do |post|
        feed.entry(post) do |entry|
          entry.title(post.title)
          entry.content(post.body, :type => 'html')
    
          entry.author do |author|
            author.name("DHH")
          end
        end
      end
    end
    

    The code should be self-explanatory (if not, please leave a comment).

    Recently, I had two such views which were almost identical, the only difference was the feed title. And that’s of course not really DRY.

    One possible approach to fix this “issue” is to set an instance variable with the feed title in the controller and render the same view in both cases. As I don’t like to set view titles (resp. feed titles in this case) in the controller, I decided to use a partial.

    And so I put the code from above into a partial (plus changing the instance variable “@posts” to a local variable “posts” and introducing a new local variable “feed_title”), and tried to use this partial (app/views/shared/_feed.atom.builder) in the following way:

    # app/views/posts/index.atom.builder
    render :partial => 'shared/feed', :locals => {:feed_title => "My Posts", :posts => @posts}
    

    Well, it didn’t work. No output was generated.

    After experimenting a bit I found the following solution by moving the outer-most block definition from the partial to the views:

    # app/views/posts/index.atom.builder
    atom_feed do |feed|
      render :partial => 'shared/feed', :locals => {:feed => feed, :feed_title => "My Posts", :posts => @posts}
    end
    

    I don’t know whether this is the best solution (probably not), but it is definitely better than what I had previously ;-)

    3 comments

  • Link-like buttons

    Sometimes you want to make a button look like a link. For example, in an admin interface you might want to have “links” to edit and delete articles. The “edit” link will be a normal link. But for the “delete” link you “cannot” use a normal link, because a delete action changes the state on the server and hence it should be performed using POST. Therefore the need for a button.

    Thanks to CSS it should be easy to make a button look like a link, that’s what I thought when I started. But as usual when I work with CSS, what seems to be easy is not that easy… Anyway, here is the solution:

    First the HTML code. Nothing special here, the only thing to note is that I had to use a “button” instead of an “input” element due to some CSS issues with Konqueror.

    <form action="/article/1" method="post">
      <p>
        <button type="submit" class="link"><span>Delete</span></button>
      </p>
    </form>
    

    And here the CSS (thanks to Natalie Downe for her article Styling buttons to look like links, from where I got some of the settings below):

    button.link {
      -moz-user-select: text;
      background: none;
      border: none;
      cursor: pointer;
      color: blue;
      font-size: 1em;
      margin: 0;
      padding: 0;
      text-align: left;
      text-decoration: underline;
      overflow: visible;
      width: auto;
    }
    

    This worked fine in Konqueror, but in Firefox there was always a padding of 2px. And of course I had no clue why. As you can see in the CSS snippet above, the padding was already set to 0… Fortunately, I found the solution in a comment of the aformentioned article:

    button::-moz-focus-inner {
      padding: 0;
      border: none;
    }
    

    To use such link-like buttons in your own application you have to adapt the CSS to your own needs.

    Have fun :)

    0 comments

© daniel hofstetter. Licensed under a Creative Commons License