Posts Tagged ‘php’

internOwl Launched!

Posted on:Wednesday, September 16th, 2009 by Matt Daum

Today we are proud to unveil  internOwl.  internOwl is a site for students to research internships and find them.  As the site grows students will be able to gain invaluable insight into the quality of different internships around the country.   Currently the site is being launched with a focus on targeting Massachusetts’ students.  We are excited to see how it performs.

If you are a student in the Amherst or Northampton area you can get a FREE burrito via the following url: http://www.internowl.com/bueno

We hope you all enjoy and there will be more updates about the site to follow as well as the technology used behind the site!

FOSS Saturday: sfFbConnectGuardPlugin – sfGuard meets FB Connect

Posted on:Saturday, September 12th, 2009 by Ashish Datta

I was slaving over a hot keyboard all Friday!

But at last it is done – FBConnect for sfGuard.

Get it here http://www.symfony-project.org/plugins/sfFbConnectGuardPlugin

A detailed explanation of how to install it and use it is on the Symfony site.

Anyway, the plugin basically just introduces a new table to keep track of Facebook IDs <---> sfGuardUserIds

Here’s a fun nugget. One of the problems with using FB Connect is that you can’t mug a user’s email address from Facebook. Obviously this is a smart move on Facebook’s part but it makes life hard for my Nigerian spammer friends. If you want to snag a user’s email address (or anything else for that matter) while still using Facebook Connect here’s a sketch of how to do it.

Everything is the same except you can’t use Facebook’s FBML to render the FB Connect button. What you want to do instead is trigger the “connect” event by hand. Here is basically how we do it:

  1. The user requests to sign up.
  2. We pop up a Lightbox using Thickbox
  3. We ask the user for their email address and verify that is valid and unique via AJAX in the background.
  4. The validation routing sets an attribute on the user using setAttribute() that contains the entered email address.
  5. We close the Lightbox and initiate a Facebook Connect request with FB.Connect.requireSession
  6. In our createFbUser() method we get the attribute back and save it with the new user

Bam. Got the user’s email address and logged them in via FB Connect.

FOSS Fridays: MacGyvered Key/Value in Symfony

Posted on:Friday, August 7th, 2009 by Ashish Datta

On a project we’re currently working on, we arrived at a situation where our client had a loose and very fluid idea of the information he wanted to store about certain objects in his application. We didn’t specifically know the number of fields or the format of the data. Continually modifying the schema would of been painful so I wanted to try something different.

Since the data is more or less non-relational (it only relates to the object that owns it), what I really wanted was an ad-hoc key/value store. But I didn’t want to break Propel’s ORM abstractions. I still wanted to be able to do:

$company->getMission();

With the new system.

Turns out you basically can. Here’s how it works:

  1. Add a “dynamic_field” table to your schema. (definition is below)
  2. Override the __call(), hydrate(), and save() functions in Propel model file that you want to MacGyver.
  3. Pray.

Definition of the dynamic_field table:






  

So the idea is we want to basically build a Propel Behaviour to capture any undefined get/set calls and “get” the data out of the dynamic_field table or “set” the data by storing the value into the table. Since the table stores the model class and model id, the “keys” only have to be unique by model (just like Propel normally works).

Here is the code you need to add to the model file:

public function __call($method, $arguments){

  // snag the dynamic setters
  if(strpos($method, "set") !== false
      && $method[3] === strtoupper($method[3])){
	  $name = strtolower( substr($method, 3) );
	  $this->dynamicFields[ $name ] = array_pop( $arguments );
	  return true;
  }

  // snag the dynamic getters
  if(strpos($method, "get") !== false
      && $method[3] === strtoupper($method[3])){
        $name = strtolower( substr($method, 3) );

      if( array_key_exists($name, $this->hydratedFields) ){
        	return $this->hydratedFields[$name];
      }

       if( array_key_exists($name, $this->dynamicFields) ){
        	return $this->dynamicFields[ $name ];
        }

      	return null;
    }

    return parent::__call($method, $arguments);
}

public function hydrate($row, $startcol = 0, $rehydrate = false)
{
  parent::hydrate($row, $startcol, $rehydrate);
  // pull in our dynamic fields while we're at it
  $c = new Criteria();
  $c->add( DynamicFieldPeer::MODEL, get_class($this) );
  $c->add( DynamicFieldPeer::MODEL_ID, $this->getId() );
  $dynamic = DynamicFieldPeer::doSelect( $c );

  foreach($dynamic as $d){
     $this->hydratedFields[ $d->getFieldName() ] = unserialize( $d->getFieldValue() );
  }

  return true;
}

  public function save(PropelPDO $con = null){

	  // save the dyanmic ones
    if( count($this->dynamicFields) ){

    	// grab the old ones and update stuff
      $keys = array_keys($this->dynamicFields);
      $c = new Criteria();
      $c->add( DynamicFieldPeer::MODEL, get_class($this) );
      $c->add( DynamicFieldPeer::MODEL_ID, $this->getId() );
      $c->add( DynamicFieldPeer::FIELD_NAME, $keys, Criteria::IN );
      $savedFields = DynamicFieldPeer::doSelect( $c );

      foreach($savedFields as $sf){
      	$sf->setFieldValue( serialize( $this->dynamicFields[$sf->getFieldName()] ) );
      	$sf->save();
      	unset( $this->dynamicFields[$sf->getFieldName()] );
      }

		  foreach( $this->dynamicFields as $key => $val ){
			  $df = new DynamicField();
			  $df->setModel( get_class($this) );
			  $df->setModelId( $this->getId() );
			  $df->setFieldName( $key );
			  $df->setFieldValue( serialize( $val ) );
			  $df->save();
		  }

	  }

	  return parent::save($con);
  }

The code captures any undefined get/set calls and then deals with them appropriately. It won’t serialize the fields until the save() call (just like regular Propel objects). I also overloaded the hydrate() function so that the object will fetch all of its dynamic fields in one shot, as opposed one query per get.

Using the modified objects is exactly like regular Propel objects, the changes are entirely transparent except that you can get/set anything you want.

For example:

$company = CompanyPeer::retrieveByPK( 5 );
$company->setVision( "this is my vision" );
echo $company->getVision();

Will work even though there is no “vision” column on the company table. Magic.

There is one big problem with this trick though. Because of the Propel class hierarchy, there isn’t any way to introduce this code in one file and have other objects inherit the changes. You have to manually copy it to any model file that you want to enable it for.

Google Calender embed missing events

Posted on:Tuesday, August 4th, 2009 by Ashish Datta

So we decided to use the Google Calendar API in one of our applications to allow users to easily view and export events from outside the app. In general, the API was working well – I was using the Zend library to interact with Google and things seemed fine.

That was until I tried to embed the calendar using Google’s iframe embed code. For some reason, events weren’t showing up in the embeded iframe calendar even though they were showing up in the actual calendar on calendar.google.com. Even stranger, the events were present in a JSON object on the embeded page and they were showing up in the RSS feed for the calendar.

After literally days of debugging and experimenting I finally found out the culprit.

For some reason, events created via the API that start and end at exactly the same time – say a start date of 08-05-2009 10:00:00 and an end date of 08-05-2009 10:00:00 don’t render on the embeded iframe calendar.

What is even more bizarre is that if you create an event via the web interface that starts and ends at the same time, it will render correctly on an embeded calendar.

Anyway, that was weird. All the events without explicit start and end times now last a grand total of one minute.

PS. Kudos to Daum for finding a constant for PHP’s date() function to generate RFC3339 timestamps.

Use like so:

  $date = date(DATE_RFC3339, $timestamp);

To get back a valid RFC3339 for the Google Calendar API.

FOSS Fridays: OpenSSL in PHP

Posted on:Friday, July 31st, 2009 by Ashish Datta

Well Twitter has “Follow Fridays” so I thought we should do FOSS Fridays. I don’t really have a plan for this and it might not last but let’s see where it goes.

In the last few days a couple of people have asked for tips on how to use OpenSSL from PHP. So here is a snippet on how to do it. This comes out of an application that provides a shared authentication system between our client’s LDAP system and their partner’s systems.

It works like so:

  1. Users login to the application using their LDAP credentials.
  2. When the users request to visit the partner site, our system packages up their login information, encrypts it, signs it, and shoots it along with the user to the partner site.
  3. Next, the partner checks if the user has an account and if they do it logs them in. Otherwise, it creates them a new account and logs them in.

All of this is done transparently so that the user doesn’t know they’ve actually left the original site.

Here is the code to do it. PS. it’s from a Symfony application.

$user = $this->getUser();

$profile = $user->getProfile();

if(is_null($profile)){ die("Could not get user profile?"); }

$email = $profile->getEmail();

$firstName = $profile->getFirstName();

$lastName = $profile->getLastName();

$password = $request->getParameter("password");

$keyText = file_get_contents(sfConfig::get("sf_root_dir") . "/" . sfConfig::get("app_their_public_key"));

$theirPublicKey = openssl_pkey_get_public($keyText);

$keyText = file_get_contents(sfConfig::get("sf_root_dir") . "/" . sfConfig::get("app_our_private_key"));

$outPrivateKey = openssl_pkey_get_private($keyText);

$arr = array();

$arr["U_EMAIL"] = $email;

$arr["U_PASSWORD"] = $password;

$arr["U_FIRST_NAME"] = $firstName;

$arr["U_LAST_NAME"] = $lastName;

$arr["RL_E"] = $this->generateUrl("ps_error", array(), true);

$arr["RL_S"] = "PRIVATE URL";

$arr["ETIME"] = time() + 60;

$queryString = http_build_query($arr);

$res = openssl_sign($queryString, $signature, $outPrivateKey);

if(!$res){ throw new sfException("Could not sign the payload!", 1); }

$t = openssl_pkey_get_details($theirPublicKey);

$t = (int) ($t['bits'] / 8 ) - 11;

$l=strlen($queryString);

$cryptPayload = '';

for ($i=0; $i<$l; $i+= $t) {

  $block = substr($queryString, $i, $t);

  if (!openssl_public_encrypt($block,$tS, $theirPublicKey)){
    throw new sfException('failed encrypt', 1);
   }

  $cryptPayload .= $tS;
}

$this->encodedSignature = base64_encode($signature);

$this->encodedData = base64_encode($cryptPayload);

The net result of all of this is an encrypted payload with the user’s credentials and a signature of the payload. The payload is encrypted with “their” public key and then signed with “our” private key. This ensures that only they can open the package and only we can generate valid signatures.

Happy Friday!

Google Calendar API create on alternate calendar

Posted on:Sunday, July 26th, 2009 by Ashish Datta

A few months ago we integrated Google calendar into an application that we built for a client. Anyway, today I sat down to customize which calendars certain events were being created on. We’re using the Zend Framework’s GData package to interact with Google Calendar and surprisingly the documentation is pretty lacking.

Specifically, I was looking to create events on a calendar that was not the “primary” calendar for a user. After poking around and experimenting, I finally got things to work.

First, you can retrieve the list of available calendars with:

$service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
$user = sfConfig::get("app_gcal_user");
$pass = sfConfig::get("app_gcal_password");

$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
self::$service = new Zend_Gdata_Calendar($client);
$calFeed = self::$service->getCalendarListFeed();

$arr = array();
foreach ($calFeed as $calendar) {
  $arr[] = array( “title” => $calendar->title->text, “uri” => $calendar->getEditLink(“alternate")->href   );
}

Next, when creating the events pass in the uri and the events will appear on the alternate calendar.

self::$service->insertEvent($event, $arr[1][“uri”]);

sfWidgetjQueryTimepickr – Symfony timepickr widget

Posted on:Monday, April 6th, 2009 by Ashish Datta

A couple of months ago John Resig posted on his blog about a “new” way for users to pick time.

The component is a jQuery plugin called timepickr and I thought it was particularly neat. Anyway, I finally needed to write a form which had a time component so I figured I’d drop in the timepickr jQuery plugin.

It didn’t look like there was a Symfony Forms widget for it so I whipped one up. You can grab it here.

The only issue with timepickr is that it introduces a ton of dependencies. It requires jQuery, jQuery-ui, jQuery.utils, jQuery.string, and ui.dropslide. Additionally, it needs the dropslide css as well as the timepickr css.

The form widget assumes that you will include jquery-ui by yourself since everyone usually has different naming conventions. It will include the other JS files and CSS files for you.

You can download a package with all of the JS and CSS you need here. Again this DOES NOT include the jQuery-ui stuff. YOU have to include that yourself in your Symfony project as well as on the page that you deploy this widget.

To use it, just drop it in your Symfony project somewhere where classes get autoloaded (projectdir/lib works) and then instantiate a widget with new sfWidgetjQueryTimepickr() . It currently will support all of the timepickr options passed in as widget options (on the constructor). Full documentation for timepickr is here.

Have fun picking time.

sfPropelPager and GROUP BY criteria

Posted on:Tuesday, February 10th, 2009 by Ashish Datta

So for one reason or another (actually a few bad ones) I ended up having to use a Criteria object looking like this:

$c = new Criteria ( );
$having = $c->getNewCriterion('the_count', count($tags), Criteria::GREATER_EQUAL);
$c->add(TaggingPeer::TAG_ID, $tags, Criteria::IN);
$c->addSelectColumn("COUNT(*) AS the_count");
$c->addSelectColumn(TaggingPeer::ID);
$c->addSelectColumn(TaggingPeer::TAGGABLE_ID);
$c->addSelectColumn(TaggingPeer::TAGGABLE_MODEL);
$c->addGroupByColumn(TaggingPeer::TAGGABLE_MODEL);
$c->addGroupByColumn(TaggingPeer::TAGGABLE_ID);
$c->addHaving($having);

It makes SQL that looks something like this:

“SELECT tag_id, tag_model, id, COUNT(*) AS the_count FROM sf_tagging WHERE tag_id IN (1,2,3) GROUP BY taggable_model, taggable_id HAVING the_count > int”

The query sucks but whatever it works.

My issue came when I tried to use it with a sfPropelPager. I set up the pager per usual but for some reason the results that were coming back weren’t correct. For some reason, the COUNT being returned by the sfPropelPager was completely wrong. It turns out the offending lines are here in sfPropelPager.class.php :

public function init()
{
  $hasMaxRecordLimit = ($this->getMaxRecordLimit() !== false);
  $maxRecordLimit = $this->getMaxRecordLimit();

  $cForCount = clone $this->getCriteria();
  $cForCount->setOffset(0);
  $cForCount->setLimit(0);
  $cForCount->clearGroupByColumns();

}

For whatever reason, sfPropelPager clears the GROUP BY clauses before it calculates the COUNT for a criteria object. I’m not sure why it does this – but it certainly is unexpected and breaks my query in particular.

There are a handful of posts about this on the Symfony forums and it looks like the Propel people know about the issue to.

The solution to this is to use the setPeerCountMethod() from sfPropelPager. The setPeerCountMethod() function allows you to specify a custom COUNT() method inside the peer for your Criteria. I went ahead and added a new function to put the GROUP BY columns back in:

public static function advancedSearchCount($c){
$c->addGroupByColumn(TaggingPeer::TAGGABLE_MODEL);
$c->addGroupByColumn(TaggingPeer::TAGGABLE_ID);
return self::doCount($c);
}

This solution works but it is extremely rigid. Since the custom count function has to be static you’d really be out of luck if you had variable columns or other dynamic requirements.

I’d love to know if someone has a cleaner/better/more elegant solution for this.

Client-side validation for the new Symfony forms with jQuery

Posted on:Friday, January 23rd, 2009 by Hamid Palo
The new Symfony forms are great improvement over the old forms and once you get over the learning curve you can’t imagine life without them. The only problem with them is that they don’t really offer client-side validation (yes, being agnostic when it comes to Javascript libraries is maybe good but come on). There is fortunately a plugin that does client-side validation but it is Prototype-based, and looks a bit clunky.
For those of us using jQuery there was nothing, so we decided to write our own small helper that integrates this jQuery validator with the new forms. It’s nothing fancy, but it does the trick for now, and we may expand it into a full-fledged plugin that does all sorts of crazy things.
Using the helper:
  1. Download and install the jQuery validation plugin.
  2. Download the helper and put it in lib/helper/jQueryValHelper.php
  3. Include the jQueryVal helper.
  4. To enable the helper for a specific form:
    <?php echo jquery_val_form_tag($form, array(‘action’ => url_for(“@register) ))?>

That’s it.

The validator currently supports email, min and max length validation. The only required option is action, the other options you can pass it are:

  1. error_placement: Change where the errors are rendered, for example:
    'error_placement' => 'error.prependTo(element.parent().next())'
  2. error_element: Change the element used to render the errors, default is label.

All other options you pass are added to the form as attributes.

More BOSS!

Posted on:Sunday, August 17th, 2008 by Ashish Datta

So I had some time (not really Daum just couldn’t get me to do any real work) so I wrote up a thin wrapper around Yahoo BOSS.

The code is available here

I mashed it into the where/what/when thing and made a swag front end ;)

The only search class implimented is the web one. I’ll probably try and do news/image later this weekend. I also haven’t tested ANY of the features so it’s very possible nothing works.