Category: "Zend Framework"

Zend Framework - Oauth - Get LinkedInContacts

Get a request token

            
            /* Set up the Oauth consumer */
            $oauth = new Zend_Oauth_Consumer(array(
                'consumerKey' => $this->configs->connections->consumerKey,
                'consumerSecret' => $this->configs->connections->consumerSecret));
            $oauth->setRequestTokenUrl($this->configs->token_uri);

            /* Get the request token */
            $token = $oauth->getRequestToken();

            /* This is the URI to allow the user to authorize access */
            $auth_uri = $token->xoauth_request_auth_url.'?oauth_token='.urlencode($token->oauth_token);

            /* Save the request token */
            $session=new Zend_Session_Namespace('linkedin_connections');
            $session->token = serialize($token);

Get the OAuth token and token secret for the user

              
                /* Set up the OAuth consumer */
                $oauth = new Zend_Oauth_Consumer(array(
                    'consumerKey' => $this->configs->connections->consumerKey,
                    'consumerSecret' => $this->configs->connections->consumerSecret));
                $oauth->setAccessTokenUrl($this->configs->access_uri);

                /* Get the request token */
                $session=new Zend_Session_Namespace('linkedin_connections');
                $token = unserialize($session->token);

                /* Get the access token and token secret */
                $token = $oauth->getAccessToken(
                    array('oauth_token' => $token->oauth_token,
                        'oauth_token_secret' => $token->oauth_token_secret,
                        'oauth_verifier' => $data['auth_code']),
                    $token,Zend_Oauth_Consumer::POST);

                $response = $token->getResponse();
                $this->linkedinconnections_data->oauth_token = $token->oauth_token;
                $this->linkedinconnections_data->oauth_token_secret = $token->oauth_token_secret;
                $this->linkedinconnections_data->oauth_expires_in = $token->oauth_expires_in;
                $this->linkedinconnections_data->oauth_authorization_expires_in = $token->oauth_authorization_expires_in;

Get the connections

            
            /* Set up the Oauth client */
            $oauth = new Zend_Oauth_Client(array(
                'consumerKey' => $this->configs->connections->consumerKey,
                'consumerSecret' => $this->configs->connections->consumerSecret),$this->configs->connections_uri);

            /* Set up the tokens for the user */
            $token = new Zend_Oauth_Token_Access();
            $token->setToken($this->linkedinconnections_data->oauth_token);
            $token->setTokenSecret($this->linkedinconnections_data->oauth_token_secret);

            /* Set the tokens for the client */
            $oauth->setToken($token);
            $oauth->setMethod(Zend_Oauth_Client::GET);

            /* Make the request */
            $response = $oauth->request();

Zend Framework Version 1.11

Token Handling - Zend Framework - OAuth 2.0 - Google

Sample code to get a request token from Google through OAuth 2.0. These are snippets of code to show the request and response interaction.

This is the link to allow a user to authorize application access.

        $auth_uri='https://accounts.google.com/o/oauth2/auth?'.
                'client_id='.$this->configs->contacts->client->id.'&'.
                'redirect_uri='.$this->configs->contacts->redirect_uri.'&'.
                'scope='.$this->configs->contacts->scope.'&'.
                'response_type=code';

If the user authorizes access, Google gives them a token, which is referred to as an auth_code in the following code. They paste the token in the auth_code input and click a button to initiate this action.

            if ($form->getElement('auth_code')->isValid($data['auth_code']))
            {
                $client = new Zend_Http_Client($this->configs->oauth_uri,
                    array( 'maxredirects' => 0, 'timeout'      => 30));
                $client->setMethod(Zend_Http_Client::POST);
                $client->setHeaders('Content-Type: application/x-www-form-urlencoded');
                $client->setParameterPost(array(
                    'code' => $data['auth_code'],
                    'client_id' => $this->configs->contacts->client->id,
                    'client_secret' => $this->configs->contacts->client->secret,
                    'redirect_uri' => $this->configs->contacts->redirect_uri,
                    'grant_type' => 'authorization_code'));
                $response = $client->request();
                $this->googlecontacts_data->last_status = $response->getStatus();
                if ($response->isSuccessful())
                {
                    $response_data = Zend_Json::decode($response->getBody());
                    $this->googlecontacts_data->last_auth = new Doctrine_Expression('NOW()');
                    $this->googlecontacts_data->access_token = $response_data['access_token'];
                    $this->googlecontacts_data->expires_in = $response_data['expires_in'];
                    $this->googlecontacts_data->token_type = $response_data['token_type'];
                    $this->googlecontacts_data->refresh_token = $response_data['refresh_token'];
                    $this->googlecontacts_data->status = 'authorized';
                }
                else
                {
                    $this->googlecontacts_data->access_token =
                    $this->googlecontacts_data->expires_in =
                    $this->googlecontacts_data->token_type =  
                    $this->googlecontacts_data->refresh_token = null;
                    $this->googlecontacts_data->status = 'not_authorized';
                }
                $this->save_googlecontacts_data(); 

This code uses the access or refresh token to retrieve the contacts.

        if ($this->googlecontacts_data->status == 'authorized')
        {
            $client = new Zend_Http_Client($this->configs->contacts->userinfo,
                array( 'maxredirects' => 0, 'timeout' => 30));
            $client->setMethod(Zend_Http_Client::GET);
            $client->setHeaders('Authorization: Bearer '.$this->googlecontacts_data->access_token);
            $response = $client->request();
            if (!$response->isSuccessful())
            {
                $client = new Zend_Http_Client($this->configs->oauth_uri,
                    array( 'maxredirects' => 0, 'timeout'      => 30));
                $client->setMethod(Zend_Http_Client::POST);
                $client->setHeaders('Content-Type: application/x-www-form-urlencoded');
                $client->setParameterPost(array(
                    'client_id' => $this->configs->contacts->client->id,
                    'client_secret' => $this->configs->contacts->client->secret,
                    'refresh_token' => $this->googlecontacts_data->refresh_token,
                    'grant_type' => 'refresh_token'));
                $response = $client->request();
            }
            $this->googlecontacts_data->last_status = $response->getStatus();
            if ($response->isSuccessful())
            {
                $response_data = Zend_Json::decode($response->getBody());
                $this->googlecontacts_data->last_auth = new Doctrine_Expression('NOW()');
                $this->googlecontacts_data->access_token = $response_data['access_token'];
                $this->googlecontacts_data->expires_in = $response_data['expires_in'];
                $this->googlecontacts_data->token_type = $response_data['token_type'];
                $this->googlecontacts_data->auto = null;
                $this->googlecontacts_data->deleted_at = null;
                $this->googlecontacts_data->status = 'authorized';
            }
            else
            {
                $this->googlecontacts_data->access_token =
                $this->googlecontacts_data->expires_in =
                $this->googlecontacts_data->token_type =
                $this->googlecontacts_data->refresh_token =
                $this->googlecontacts_data->auto = null;
                $this->googlecontacts_data->status = 'not_authorized';
            }
            $return = $this->save_googlecontacts_data();
            if (!isset($return['error']))
            {
                $this->view->results = $this->get_contacts();
                $this->return['success'] = true;
            }
        }
        else
            $this->return['error'] = $this->status();

Some of the config values (other omitted for security):

oauth_uri = “https://accounts.google.com/o/oauth2/token”
contacts.uri = “https://www.google.com/m8/feeds/contacts/default/full”
contacts.scope = “https://www.google.com/m8/feeds/”
contacts.userinfo = “https://www.googleapis.com/oauth2/v1/userinfo”

.ini file settings for auth_code input. This application forces the user to cut and paste the token into the browser.

[production]
action="/contacts/google”
method="post”

disableTranslator = 0
; code element
elements.auth_code.type = “ValidationTextBox”
elements.auth_code.options.label = “Authorization Code”
elements.auth_code.options.required = true
elements.auth_code.options.trim = “true”
elements.auth_code.options.class = “long”
elements.auth_code.options.validators.strlen.validator = “StringLength”
elements.auth_code.options.validators.strlen.options.min = “8″
elements.auth_code.options.validators.strlen.options.max = “100″
elements.auth_code.options.validators.regex.validator = “regex”
elements.auth_code.options.validators.regex.options.pattern = “/^[\w\/\-]{8,100}$/”
elements.auth_code.options.validators.regex.options.messages.regexInvalid = “Invalid code”
elements.auth_code.options.filters[] = “StringTrim”
elements.auth_code.options.filters[] = “StripTags”
elements.auth_code.options.filters[] = “StripNewlines”

displayGroups.gcode.options.order = 10
displayGroups.gcode.options.class = “auth_code”
displayGroups.gcode.elements[] = “auth_code”

Zend Framework 1.11 - Rename Form Elements

Workaround

In this case, the method accepts the name of the element and applies a prefix.

The code was derived from Zend_Form::removeElement.

private function prefixElement($name)
        {
            $name = (string) $name;
            if (isset($this->_elements[$name])) {
                $this->_elements[$name]->setName($this->_prefix.$name);
                $this->_elements[$this->_prefix.$name]=$this->_elements[$name];
                unset($this->_elements[$name]);
                if (array_key_exists($name, $this->_order)) {
                    $this->_order[$this->_prefix.$name]=$this->_order[$name];
                    unset($this->_order[$name]);
                    $this->_orderUpdated = true;
                } else {
                    foreach ($this->_displayGroups as $group) {
                        if (null !== $group->getElement($name)) {
                            $group->addElement($this->getElement($this->_prefix.$name));
                            $group->removeElement($name);
                        }
                    }
                }
                return true;
            }

            return false;
        }

Zend Framework - No translation for the language 'en_US' available.

Notice: No translation for the language ‘en_US’ available. in /var/www/html/ZendFramework-1.10.8-minimal/library/Zend/Translate/Adapter.php on line 441

I’ve been working on an application using Zend Framework as the foundation. One of the key elements is ensuring internationalization/i18n support, and the error above was being displayed.

In addition to translating page text, I wanted to add htmlentities
conversion, without calling it explicitly.

I created an adapter which extends the Gettext adapter.


<?php

Class CLOUD_Translate_Adapter_Gettext Extends Zend_Translate_Adapter_Gettext
{
    public function translate($messageId, $locale = null)
    {
		return htmlentities(parent::translate($messageId,$locale),ENT_QUOTES,'UTF-8');
	}
}

To prevent the error from being displayed, I changed:

        
$translate = new Zend_Translate(array('adapter'=>'CLOUD_Translate_Adapter_Gettext',
                'content'=>$language_path,
                'scan' => Zend_Translate::LOCALE_DIRECTORY,
                'locale'=>$locale->toString()));
$translate->setOptions(array('disableNotices'=>true));

to:


$translate = new Zend_Translate(array('adapter'=>'CLOUD_Translate_Adapter_Gettext',
                'content'=>$language_path,
                'scan' => Zend_Translate::LOCALE_DIRECTORY,
                'locale'=>$locale->toString(),
                'disableNotices'=>true));

The difference is that disableNotices is included in the instantiation, so that as it initializes the translation object, detected errors are not reported.

Since the default language for the page is en-US, there is no need for translation.

Zend Framework Admin Form extension of Zend_Dojo_Form

The objective of this class is to provide a foundation class for admin forms which provides the following:

  • Security - a hash input is used. It must return to the server with the same value sent to the client.
  • Multi-form pages - this class prefixes all inputs so it can be used to generate forms which coexist on the same page. validate, getValues and populate have an additional parameter to support this, indicating whether or not to use the prefix.
  • Greater transaction safety - the version number of the data is sent to the client, and tested later to ensure the data was not changed prior to saving. This test is performed in the Doctrine code, using a Record_Listener which tests the version submitted against the current record version and then increments it prior to save if valid, or throws an exception in the case of a mismatch.
  • ACL support - the form reviews the array of buttons set in the extension class to ensure the administrator has adequate privileges.

<?php
	// application/forms/Account.php
	class Admin_Form extends Zend_Dojo_Form
	{
		protected $_security = null;
		protected $_buttons = null;
		protected $_hash = null;
		protected $_prefix = '';

		public function init()
		{	
			$front = Zend_Controller_Front::getInstance();
			$this->setAction('/'.$front->getRequest()->getParam('module').'/'.$front->getRequest()->getParam('controller'));
			$security = array();
			$security['hash'] = new Zend_Form_Element_Hash('hash');
			$security['hash']->setOptions(array('salt'=>'unique'));
			$security['id'] = new Zend_Dojo_Form_Element_TextBox('id');
			$security['version'] = new Zend_Dojo_Form_Element_TextBox('version');
			$security_names = array();
			foreach ($security as $k => $v)
				$v->setRequired(true)
					->addValidator('Identical')
					->addFilter('StringTrim');
			$this->addElements($security);
			$this->_security = $security;
			$this->addDisplayGroup(array_keys($security),'hsh',array('order'=>0,'style'=>"display:none"));
			if ($this->_buttons != null) 
			{
				$acl_buttons=array();
				$buttons_ok=false;
				if (Zend_Auth::getInstance()->hasIdentity())
        		{
		            $identity = Zend_Auth::getInstance()->getIdentity();
        		    $role = $identity['role'];
				}
				else
					$role = 'none';
				$acl = Zend_Registry::get('Zend_Acl');
				$resource = 'mvc:'.$front->getRequest()->getParam('module').'.'.$front->getRequest()->getParam('controller');
				$buttons = $this->_buttons;
				foreach ($buttons as $k => $v)
					if ($acl->has($resource) && $acl->isAllowed($role,$resource,$k))
					{
						$this->addElement($v);
						$buttons_ok=true;
					}
				if ($buttons_ok) 
					$this->addDisplayGroup(array_keys($buttons),'buttons',array('order'=>100,'class'=>'buttons'));
			}
			parent::init();
		}

		protected function setPrefix($prefix)
		{
			$prefix .= '_';
			$this->_prefix = $prefix;
			$elements = $this->getElements();
			foreach ($elements as $k => $v)
			{
				$v->setName($prefix.$k);
				$this->prefixElement($k);
			}
            $display_groups = $this->getDisplayGroups();
            foreach ($display_groups as $k => $v)
				$v->setName($prefix.$k);		
			$this->_hash = $prefix.'hash';
			foreach ($this->_security as $k => $v)
			{
				$this->_security[$prefix.$k] = $v;
				unset($this->_security[$k]);
			}
			/* Avoids duplicate id in XHTML for hash */
			$fix = $this->getElement($prefix.'hash');
			$tag = $fix->getDecorator('HtmlTag');
			$tag->setOption('id','dd_'.$prefix.'hash');
			foreach ($this->_buttons as $k => $v)
			{
				$this->_buttons[$prefix.$k] = $prefix.$k;
				unset($this->_buttons[$k]);
			}
		}

		public function getElement($name)
		{
			$element = parent::getElement($this->_prefix.$name);
			if ($element != null)
				return $element;
			/* Fallback */
			$element = parent::getElement($name);
			if ($element != null)
				return $element;
			return null;
		}

		public function populate($values,$use_prefix = false)
		{
			self::_prefixProcessor($values,$use_prefix);
			parent::populate($values);
		}

		private function _prefixProcessor(&$data,$use_prefix = false)
		{
            $prefixed = false;
            if (is_array($data) && (count($data)>=1))
            {
                reset($data); $key = key($data);
                $prefixed = (strpos($key,$this->_prefix) === 0);
            	if ($use_prefix)
				{
					if (!$prefixed)
		            {
						$new_data = array();
        	    	    foreach ($data as $k => $v)
            	    	    $new_data[$this->_prefix.$k] = $v;
						$data = array();
						$data = $new_data;
	    	        }
				}
				else
					if ($prefixed)
					{
						$prefixLength = strlen($this->_prefix);
                        $new_data = array();
                        foreach ($data as $k => $v)
                            $new_data[substr($k,$prefixLength)] = $v;
                        $data = array();
                        $data = $new_data;
					}
			}
		}

		public function setTokens()
		{
            if (($this->_hash == null) || ($this->_security == null)) return;

            $session_security=$this->getElement($this->_hash)->getSession();
            $security = $this->_security;
            foreach ($security as $k => $v)
				$session_security->$k = $this->getElement($k)->getValue();
		}

		public function getTokens()
		{
			if (($this->_hash == null) || ($this->_security == null)) return;

            $session_security=$this->getElement($this->_hash)->getSession();
			$security = $this->_security;
			foreach ($security as $k => $v)
			{
				$validator = $this->getElement($k)->getValidator('Identical');
				if (isset($session_security->$k))
					$validator->setToken($session_security->$k);
				else
					$validator->setToken('');
			}
		}

		public function getValues($data = null,$prefixed = false)
		{
			$return = parent::getValues();

			if ($this->_buttons != null)
				$return = array_diff ($return,$this->_buttons);

			self::_prefixProcessor($return,$prefixed);

			return $return;
		}

		private function prefixElement($name)
	    {
    	    $name = (string) $name;
        	if (isset($this->_elements[$name])) {
				$this->_elements[$name]->setName($this->_prefix.$name);
				$this->_elements[$this->_prefix.$name]=$this->_elements[$name];
	            unset($this->_elements[$name]);
    	        if (array_key_exists($name, $this->_order)) {
					$this->_order[$this->_prefix.$name]=$this->_order[$name];
        	        unset($this->_order[$name]);
            	    $this->_orderUpdated = true;
	            } else {
    	            foreach ($this->_displayGroups as $group) {
        	            if (null !== $group->getElement($name)) {
							$group->addElement($this->getElement($this->_prefix.$name));
            	            $group->removeElement($name);
	                    }
    	            }
        	    }
	            return true;
    	    }

        	return false;
	    }

	}

Doctrine code to test and update the version number. This is added in to the model for the data with addListener.

<?php
class Record_Listener extends Doctrine_Record_Listener
{
    public function preUpdate(Doctrine_Event $event) 
	{
		$invoker = $event->getInvoker();
		if (array_key_exists('version',$invoker->getModified())) 
		{
			$invoker->getErrorStack()->add('version','match');
			throw new Doctrine_EventListener_Exception('Version not identical');
		}
		/* Prevents modification of uuid */
		unset($invoker->uuid);
		$invoker->version++;
	}

	public function preDelete(Doctrine_Event $event)
	{
		$invoker = $event->getInvoker();
		if (array_key_exists('version',$invoker->getModified()))
		{
			$invoker->getErrorStack()->add('version','match');
			throw new Doctrine_EventListener_Exception('Version not identical');
		}
	}
}

This an the extension of the Admin_Form. It sets the name, configuratino from the user.ini file, buttons array, and the prefix.

<?php
	// application/forms/Account.php
	class Admin_User_View_Form extends Admin_Form
	{
		public function init()
		{
			$this->setName('frmUserView');
			$user = new Zend_Config_Ini(APPLICATION_PATH."/configs/admin/user.ini",'production',true);
			$this->setConfig($user);
			$this->_buttons = array();
			$this->_buttons['save'] = new Zend_Dojo_Form_Element_Button('save');
			$this->_buttons['save']->setOptions(array(
				'name' => 'save',  
				'label' => 'Save',
				'value' => 'save',
				'onclick' => 'user_save()'));
			parent::init();
			$this->setPrefix('user');
		}
	}

Sample view implementation. $user is a Doctrine object with the data. This is an AJAX form, so the json helper is used. Note the call to setTokens, which sets the hash value and saves the security elements in a session variable for later validation.


			$data = $user->getData();
			$form = new Admin_User_View_Form();
			$form->populate($data);
			$hash = $form->getElement('hash')->getHash();
			$form->setTokens();
			$values = $form->getValues(null,true);
			unset($values['user_password']);
            foreach ($values as $k => $v)
                $values[$k] = $this->view->escape($v);
			$this->_helper->json($values);

Sample save implementation, from the same controller. Note the getTokens call which retrieves the saved session data for the form.


			$form = new Admin_User_View_Form();
			$form->getTokens();
			$data = $this->getRequest()->getPost();
                        if ($form->isValidPartial($data))
                        {
                            $form->populate($data,true);
                            $user->fromArray($form->getValues($data,false));
                        }
			else
				$this->_helper->json(array('form'=>$form->processAjax($data)));

Supporting javascript is omitted, because it’s really application specific. One note is that the hash input isn’t a dojo/dijit input, so it must be handled manually with dojo.byId, instead of auto loaded with the form values.

This has been updated to work with Zend Framework 1.11.

1 2 3