Code

Introducing 20Couch - The Bastard Child of Twitter and Google Reader

Posted by Matt on Tue, Mar 23 2010

20Couch is a self-hosted PHP/MySQL Twitter client.  The interface is very similar to Google Reader.  I won't say they stole UI, but the similarities are a little too much to be a coincidence.  Do no evil?  Really Google?  Anyway, what better way to announce a new app then a fake interview? Off we go!

Let's get right to it.  You're charging $30 for a Twitter client?  Where you born crazy or was it something that developed after numerous head traumas?
I went back and forth on trying to sell this or just tossing it on Github about 50 times.  I don't have a strong reason for my eventual decision to sell it other then the need to feed my ferocious heroin addiction.  As for the selling price of $30?  It just felt right and it seemed to be the going rate for self-hosted web apps.  There is also a free option - you just need to agree to write a review.  This offer is pretty much open to anyone with a blog (except for TechCruch - they suck).  There aren't any restrictions on what your write. To quote myself: "You can say it sucks if you want. I'll probably tear up a bit and drive by your house like a hundred times, but I won't try to stop you."


Why another Twitter client? 
I figured I had to do either a Twitter client or a PHP framework or lose my developer cred.  I had been using the Echofon Firefox add-on, but was annoyed that the first thing I would do is scroll down until I found some tweets that were familiar.  Then I'd read up to catch up.  This was pain since I jumped computer a lot (personal laptop, work laptop, family computer) and it just seemed like such a hassle to keep them in sync and have to install the client everywhere I went.  A web app made so much more sense.

 

Seems like that means a lot of work just to setup a Twitter client?

It's really no different then setting up WordPress or any other PHP app.  I tried to make the installer as simple as possible.  There is also an online demo (username: 20couch, password: isawesome) that you can play with to see what it's all about before taking the leap.


What about the hosted Twitter clients like Seesmic and HootSuite?
I'm sure those guys are awesome and to be honest I haven't tried either.  I was already knee deep in 20Couch when those two started taking users.  Those clients are probably great for people who can put up with ads (or ad block) and don't mind having yet another company tracking their activity while keeping all your records in their DB.


What does 20Couch bring to the table?
The complete feature list is here.  My favorite things (in addition to not having to sync) are the search, reply reminders and following searches.  And tweets are sorted oldest to newest.  Unless you're the guy from Memento it makes no sense to read from newest to oldest.

 

Why is the iPhone/Mobile layout in beta?
I don't have a smart phone.  If there was a mobile phone Special Olympics my phone would get like 10 medals. Not because it won them, but because everyone gets a medal.  So, I do the best I can with emulators and shrinking my browser, but it isn't the same as actually using it day to day.  Soon I'll be upgrading and the mobile support will get much better.

 

What features are coming up?
There is a lighthouse project for new features and bugs.  The two big ones I want to do are plugin support and support for other networks such as identi.ca/StatusNet and Facebook.

 

Anyone you need to thank?

The app is written on CakePHP and makes heavy use of jQuery, so huge thanks to those two teams.  I'd also like to thank everyone who beta tested and provided feedback, especially @gcarreno and @rickguyer who made a ton of awesome suggestions and not only found bugs, but took the time to really dig into them.  Way above and beyond anything I expected.

 

Anything else?

The site is 20couch.com and also on Twitter at @20couch.  If I missed a question go ahead and leave it in the comments of this post and I'd be happy to answer.

Posted in Code | 8 Comments

CakePHP Progress Bar Shell Task

Posted by Matt on Wed, Mar 17 2010

I haven't posted any new code in awhile, so I figured I'd share this simple progress bar I use when running long CakePHP shells.

Get It

The code is available on GitHub.

Progress Bar Pr0n

Here's what it looks like in action:

Usage

1. Include the task in your shell. Even though this is a plugin you don't need to use the "PluginName.TaskName" format. Cake finds it somehow. It's magic.

var $tasks = array('ProgressBar');

2. Before whatever your looping through, start the progress bar by passing in the total number of whatever you're doing.

$this->ProgressBar->start(count($loops));

3. Then each time you go through the loop call next:

$this->ProgressBar->next();

Calling ProgressBar::next() will increment the bar one unit. You can also pass in a value to increment more then one unit. For example is you are processing a file you could pass the total file size in at the start and then for each chunk pass how much you processed.

$this->ProgressBar->next(strlen($chunk));

You can also set the progress bar to any point by calling ProgressBar::set(). This isn't a percentage. It's the "x of total" that is done. So if you set the total to 150 and call set with a parameter of 50 it will show the progress bar as 1/3 done.

$this->ProgressBar->set(50);

Done

This post is now 100% complete.

Posted in Code | 11 Comments

Improved JavaScript Validation (repost)

Posted by Matt on Sun, Jan 31 2010

This article was written by me and originally published on February 12, 2008 on PHP Architect's C7Y site.  Since that site is no longer around (meaning this article is currently homeless) and their one year exclusive rights expired long ago, I figured why not re-post it here.  The concepts and code in this article eventually evolved into my CakePHP JS Validation Plugin.

 

Your social video ad widget mashup is ready to be unleashed to the public.  Conscious of all the latest SQL injection and cross scripting attacks, you've locked down every form field.  Before the big launch, you enlist your buddy/co-worker/significant to do some user testing. 

After being impressed with your innovated use of a gradient within a gradient, they manage to find the user registration form.  It's just two fields, username and password.  What could go wrong here?

Your tester is feeling spunky and enters the username "D'shiz" and clicks the Join button.  The submission is whisked off across the internet to your co-located server where your stringent validation catches the disallowed apostrophe in the username.

You give a small fist pump when you see the form returned.  Across the top is a concise, highlighted error message detailing the problem.  To make sure there is absolutely no confusion you also mark the username label red.  Your tester has no trouble figuring out the issue and changes their username to "Dshiz".  Again the form is sent and again it is returned.  This time with the error "Your username must be at least 6 characters long".  Your tester changes their identity to "Dshiz49" and re-re-submits.  Ooops.  They forget to re-enter their password.  Your app had been clearing it out every time as part of your uber-security approach.

Finally the fourth submit gets it right, but in the process you wasted three page loads and the precious time of your test user.  So how can we improve this scenario?  Ajax validation is one option, but that can be overkill for many tests.  None of the rules used in this example need the server or database to validate.  If you were checking the availability of a username, that would be another story.

 

JavaScript validation has been around for years, but it's often poorly done to the point where it detracts, rather than add, value.

Let's lay out some requirements for good JavaScript validation.

Seamless To The Developer

First, keep the programmers happy.  It doesn't make sense to maintain two sets of validation rules.  Instead pull the rules from the same source that is used for server side validation and convert them to JavaScript for the client side.  Obviously not all rules can be applied with JavaScript, so you'll need to filter them.   If your rules are a mix of functions and regular expressions, you can use PHP's function_exists function to get just the regular expressions.

Seamless To The User

JavaScript alerts are not seamless.  They are annoying.  To be seamless, the feedback to the user must look the same as if the user submitted the form and the server responded.  In this scenario, we'll assume you're using an unordered list with the id "errors" to display the messages.The first step to building re-useable client side validation is organizing your validation rules.  Regular expressions are great, since they can handle many different validations and work in both PHP and JavaScript.  Here a couple that will help us in the example above.

define('RULE_NOT_EMPTY', '.{1,}');
  define('RULE_ALPHANUMERIC', '^[\w\d]{0,}$');

Hopefully you have a model class with all your rules defined or some other method of keeping them organized.   Unfortunately, everyone's system will be different, so for this article we'll want to get the rules in a keyed array with the format RULE => MESSAGE, such as:

$validation = array(
    'username' => array(
      RULE_NOT_EMPTY => 'You must enter a username.',
      RULE_ALPHANUMERIC => 'You may only use letters and numbers in your username.',
      '.{6,}' => 'Your username must be at least 6 characters.'
    ),
    'password' => array(
      RULE_NOT_EMPTY => 'You must enter a password',
      RULE_ALPHANUMERIC => 'You may only use letters and numbers in your username.',
      '.{6,}' => 'You password must be at least 6 characters'
    )
  );

Don't worry if your validation scheme doesn't match, you can always write a function to convert from your format.  Notice that in addition to the shared rules, we can add a custom rule created just for a particular field.  To simplify the JavaScript and handle cross browser issues, I'll use the jQuery library.   The JavaScript part works in two pieces.  First, a static JavaScript function that will take in the form and the rules and do the validation.  This will live in an external js file, such as common.js.

function validateForm(form, rules) {
    //clear out any old errors
    $("#errors").html("");
    $("label").removeClass("error");
    
    //loop through the validation rules and check for errors
    $.each(rules, function(field) {
      $.each(this, function(rule) {
        var val = $.trim($("#" + field).val());
        var exp = new RegExp(rule);
    
        //check if the input exists and violates rule
        if ($("#" + field).attr("id") != undefined && !val.match(exp)) {
          //add the error message
          $("#errors").append("
  • " + this + "
  • "); //highlight the label $("label[for='" + field + "']").addClass("error"); } }); }); if($("#errors").html() != "") { return false; } return true; }

    The second JavaScript part is the rules for the form that is being displayed.  This code is generated by a PHP function and included directly in the html output of your page.The form described above is pretty basic.  It could look something like this:

    <form id="appFrom" action="#" method="post">
        <div>
          <label for="username">Username</label>
          <input id="username" name="username" type="text" />    
        </div>    
        <div>      
          <label for="password">Password</label>      
          <input id="password" name="password" type="password" />    
        </div>    
        <div class="submit">     
          <input name="submitForm" type="submit" value="Submit" />      
        </div>  
      </form>

    We want to apply the rules described above to this form, in a way that is unobtrusive to the original code.  First thing we'll need to do is convert the PHP rules array to JavaScript.  This can be done with the PHP function json_encode.  PHP's JSON functions are included as of version 5.2.  For older versions you can use the JSON PECL extension or code your own version of the function.

    The other critical part is to trap the submit of the form.  You could do that by altering the form tag with an onsubmit, but instead we'll use jQuery's "submit" function, which can be placed anywhere in the html.  Using JavaScript validation does not require the user to have JavaScript on their browsers.  User's without JavaScript support will simply submit the form as normal and the server will handle the validation.

    These two requirements can be encapsulated in a single PHP function.

    function clientValidation($rules, $formId) {  
        $output = array();
        $output[] = '<script type="text/javascript">';    
      
        //convert the rules array into javascript  
        if (function_exists('json_encode')) {
          $output[] = 'var rules = eval(' . json_encode($rules) . ');';
        } else {    
          exit('This code requires PHP JSON support');     
        }  
      
        //catch the form submission  
        $output[] = '$(document).ready(function() {';
        $output[] = '$("#' . $formId . '").submit( function() {'; 
        $output[] = 'return validateForm($(this), rules)';
        $output[] = '});';  
        $output[] = '});';    
        $output[] = '</script>';    
        echo implode("\n", $output);
      }

    To use the JavaScript validation on our form we only need to include the external JavaScript file and call the function above.

    clientValidation($validation, 'appFrom');

    On the client side, when the user submits the form, it is intercepted by the new validation routine.  The supplied rules are looped through, and if there was a matching input field the rules are applied.  Fields that fail a particular test have their labels assigned the CSS class "error".  The message is also added as a <li> element to the errors list. 

    At the end of the validation, if any of the fields failed, the form is stopped from being submitted.  The user can then fix the errors and attempt to re-submit, which is the same process as presented in the original scenario.  Now however the advantages are that the errors are shown instantaneously, as there is no longer any server interaction or page loads.

    We now have a powerful, re-usable client side validation system that can be included in single PHP scripts or converted for MVC frameworks. 

    This type of validation should be used in addition to server side validation.  This code does not protect against users without JavaScript support or users who are attempting malicious attacks.

    Posted in Code | Leave a comment

    Adding Ajax Panels to the CakePHP Status Plugin

    Posted by Matt on Thu, Aug 13 2009

    Previously On...

    Last time we made a simple panel that showed the last 10 users that signed up for you web app. It was a good example for using the requestAction approach (or as AD7six pointed out you can skip the requestAction part and go right to the model).

    Let's Get Ajaxy Up In This Mofo

    This time we'll do a panel that shows the number of sign ups based on a time frame. We'll add a drop down that let's you select day, week, month or year and it'll show a breakdown of new users for that period. The updates will use ajax.

    The Element

    Just like last time we're going to create a new element: /app/views/elements/panels/signups.ctp. This time, however, the element will contain a form:

    <?php echo $form->create('User', array('id' => 'SignupsForm')); ?>
    <h1>
      <?php __('Signups'); ?>
      <?php echo $form->input('signup_span', array('type' => 'select', 'div' => false, 'label' => false,
                                                   'options' => array('day' => 'day',
                                                                      'week' => 'week',
                                                                      'month' => 'month',
                                                                      'year' => 'year')));
      ?>
    </h1>
    <?php echo $form->end(); ?>
    
    <div id="signup-table">
      <p><?php echo $html->image('/status/img/ajax-loader.gif') ?></p>
    </div>
    

    In addition to the form, there is a div (signup-table) that will be used to hold the result of the Ajax call. For now we'll just stick a spinny icon in there.

    The JavaScript

    <script type="text/javascript">
      $(function(){
        $("#UserSignupSpan").change(function() {
          $("#signup-table").html("<p><img src=\"/status/img/ajax-loader.gif\" \></p>");
          $.get("/users/signups/" + $(this).val(), function(data) {
            $("#signup-table").html(data);
          });
        }).change();
      });
    </script>
    

    Pretty standard jQuery. We catch the change in the span select, set the holder div to the spinny icon and make the request for the updated data.

    The Controller

    function signups($span=1) {
      if (!Configure::read('Status.allow')) {
        die;
      }
    
      switch ($span) {
        default:
        case 'day':
          $field = 'SUBSTR(created, 1, 13)';
          $limit = 24;
          break;
        case 'week':
          $field = 'SUBSTR(created, 1, 10)';
          $limit = 7;
          break;
        case 'month':
          $field = 'SUBSTR(created, 1, 7)';
          $limit = 12;
          break;
        case 'year':
          $field = 'SUBSTR(created, 1, 4)';
          $limit = 5;
          break;
      }
    
      $data = $this->User->find('all', array('fields' => array($field . ' AS span', 'count(*) as cnt'),
                                             'group' => 'span',
                                             'order' => 'span DESC',
                                             'limit' => $limit));
    
      $this->set('data', $data);
    }
    

    In practice I would move most of this code to a custom find type, but for the sake of this example I'll put it all in the controller. An important note: Unlike the example in the previous post, this action has a view associated with it. That means you can access it directly without going through the status panel. To protect against that we add the check at the top to make sure the user is allowed to access the status panel.

    Include The New Panel

    Remember to add the panel to the list of panels in bootstrap.php:

    Configure::write('Status.panels', array('panels/signups'));
    

    What It Looks Like

    signups_panel

    Posted in Code | Leave a comment

    Adding Custom Panels to the CakePHP Status Plugin

    Posted by Matt on Thu, Aug 06 2009

    Intro

    A few weeks ago I released a status plugin for CakePHP. It is basically a single page where you can get a quick snapshot of your app. The plugin ships with a few random panels, but it's easy enough to add your own. This post will tell you how.

    Make sure to get the latest version from GitHub. Also note that the way to include the default panels has changed. You now have to include the "Status." prefix.

    requestAction vs Ajax

    There are two different ways to add a panel - using requestAction or Ajax. The general rule is if the panel is pretty fast to load and content is static use requestAction. If the panel takes some time or you want to be able to interact with it then use Ajax. For example, the logs panel uses requestAction since it's pretty quick to grab the log entries and you really don't need to interact with them. The Google Analytics panel, on the other hand, takes a few seconds to query the API and has options to change the timeframe, so it uses Ajax.

    Packaging Your Panel

    Panels can be packaged as plugins or just files in your app. For this example we'll just put the files as part of the app. Making your panel as a plugin is pretty much the same, you just need to put all the files in the plugin's directory and include the plugin name as a prefix when setting which panels to use. Check out the click plugin for an example. I'm just going to cover the requestAction approach today. Look for another post of using Ajax soon.

    The requestAction Way

    The Element

    Let's build a panel that shows the last 10 users that signed up for your app. The first thing you need is an element. In /app/views/elements/panels (note the extra directory level - this is optional, but helps to keep things clean) create a file called 'latest_users.ctp'. In that file put:

    <?php
      $users = $this->requestAction(array('controller' => 'users', 'action' => 'latest'));
    ?>
    

    The Controller

    In the users controller we now need to add an action called latest that will get the user data and pass it back to the element.

    function latest() {
      return $this->User->find('all', array('order' => 'created desc', 'limit' => 10));
    }
    

    Since there is no view associated with this action anyone trying to access it directly with just get an error. If you wanted to extra sure that no one unauthorized could exploit this action you could add this to the beginning:

    if (!Configure::read('Status.allow')) {
      die;
    }
    

    Back To The Element

    Now you have a list of $users so it's just a matter of displaying them. Here's the complete element:

    <?php
      $users = $this->requestAction(array('controller' => 'users', 'action' => 'latest'));
    ?>
    
    <h1><?php __('Latest Users') ?></h1>
    <?php if($users) { ?>
      <table>
        <?php foreach($users as $user) { ?>
          <tr>
            <td><?php echo $user['User']['email'] ?></td>
            <td><?php echo $time->timeAgoInWords($user['User']['created']) ?></td>
          </tr>
        <?php } ?>
      </table>
    <?php } else { ?>
      <p>Your app has no users...loser!</p>
    <?php } ?>
    

    Including Your Panel

    Now you just have to include your panel on the status dashboard. In /app/bootstrap.php add the line:

    Configure::write('Status.panels', array('panels/latest_users'));
    

    Here's What It Looks Like

    status-latest-users

    Posted in Code | 2 Comments
    1 | 2 | 3 | 4 | 5 | 6