![]() |
|
Snippets |
|
you can create a multi-file uploader in Flash using ActionScript's FileReference and FileReferenceList classes, but you have to jump through some hoops to make it work. The upload() method doesn't send the user's session cookie with the request, so you have to send it in the URL and set the cookie manually on the server. If you are using ufo.js, the first argument to UFO.create() should look like this:
{
movie: '/flash/uploader.swf',
id: 'uploader',
name: 'uploader',
flashvars: 'cookie=' + document.cookie,
// other options ...
}
If you are dealing with multilple cookies, you need to parse document.cookie and send only the desired cookie.
Next, add the cookie to the URL:
var list:Array = myFileRefList.fileList; var item:FileReference; var url:String = _root.uploadURL + '?cookie=' + _root.cookie; for (var i:Number = 0; i < list.length; i++) { item.upload (url); }
Now add the following class to your symfony project:
class mySessionStorage extends sfSessionStorage { public function initialize($context, $parameters = null) { if ( /* whatever the condition is when we want to do this */ ) { if ($cookie = $context->getRequest()->getParameter('cookie')) { $name = 'symfony'; preg_match('/^' . $name.'=(.*)$/', $cookie, $asMatch); $value = $asMatch[1]; session_name($name); session_id($value); } } parent::initialize($context, $parameters); } }
Now edit factories.yml:
all:
...
storage:
class: mySessionStorage
param:
session_name: symfony
And you're done!
My blog post on this subject seemed popular, so I made it into a snippet. Hope it helps!
If you're building a site with a user login system (like Askeet) and your PHP is configured to store session variables in a client-side cookie, the following snippet will improve the usability for users who have disabled cookies.
The following example assumes you already have at least a simple user login system on your site. If not, check out the Askeet tutorial for a great example to get you started.
All users who have disabled cookies will be unable to log into any site that relies on client-side cookies to store session variables. If you don't validate cookies and provide notification, these users will never know why they couldn't log in to your site.
Try the following if you'd like to see this firsthand.
Unfortunately, Askeet also provides the perfect example here as well. (Sorry guys!)
Not much happened, right? You're not logged in and you don't know why. (Well, you do now.)
What we're going to do is augment an existing login system to provide users with notification that your site requires cookies.
To do this, we're going to attempt to set a cookie on the login page and verify that it was created once the login form is submitted. Since a cookie is stored only after the page is loaded, it takes two pages to validate the user's setting. Fortunately, the login process takes two pages!
Edit your existing login action code by adding the following two setCookie() functions.
The first setCookie() sets the validation cookie when the login action is first loaded. (If you use your login on several pages as a component/partial, move this first setCookie() to the login form component.)
The second setCookie() deletes the validation cookie upon successful login. If this cookie is not deleted, the validation could return a false positive if the user disables cookies at a later date. (When cookies are disabled, new cookies can't be written, but old cookies can be read.)
module_dir/actions/actions.class.php:
public function executeLogin() { if ($this->getRequest()->getMethod() != sfRequest::POST) { sfContext::getInstance()->getResponse()->setCookie('cookies_enabled', 'true', time()+60*60*24, '/'); } else { sfContext::getInstance()->getResponse()->setCookie('cookies_enabled', '', 0, '/'); $this->redirect('@homepage'); } }
Enable cookie validation by adding the following code to your existing login validation. The validator takes two parameters, cookie_name and cookie_error. The cookie_name parameter is, oddly enough, the name of the cookie we'll use to validate and it should match the cookie set in the above code.
The other parameter, cookie_error, is the error message that will be returned if the user has cookies disabled. Typically, validation errors are phrases like "Invalid username" or "Password must contain 6-8 characters". But we're going to use "cookies_disabled" and I'll show you why in a few minutes.
IMPORTANT: The cookie validation should occur first.
module_dir/validate/login.yml:
methods: post: [username] names: username: required: true required_msg: validators: [cookiesEnabledValidator, userValidator] cookiesEnabledValidator: class: myCookiesEnabledValidator param: cookie_name: cookies_enabled cookie_error: cookies_disabled userValidator: class: myLoginValidator param: password: password username_error: Invalid username. password_error: Invalid password.
Copy the following code to one of your lib directories. Since it only deals with the login action, I choose to keep it in my user module's lib directory.
module_dir/lib/myCookiesEnabledValidator.class.php:
class myCookiesEnabledValidator extends sfValidator { public function initialize($context, $parameters = null) { // initialize parent parent::initialize($context); // set defaults $this->setParameter('cookie_name', sfContext::getInstance()->getStorage()->getParameter('session_name')); $this->setParameter('cookie_error', 'This site requires cookies.'); $this->getParameterHolder()->add($parameters); return true; } public function execute(&$value, &$error) { if (sfContext::getInstance()->getRequest()->getCookie($this->getParameter('cookie_name')) === null) { $error = $this->getParameter('cookie_error'); return false; } return true; } }
Now, since it takes two pages to set and read a cookie it wouldn't make sense to return the user to the form right away. If they enable their cookies they'll still have to submit the form twice before they'll login successfully. I prefer to send them to a page which notifies them that the site requires cookies and explains how they can enable them. (Plus, this gives us the extra click we need to set that validation cookie!)
So we'll check for the cookie validation error before returning to the form so we can redirect the user to our help page if necessary. The error we check for in the username parameter must match the cookie_error we defined in the login.yml.
module_dir/actions/actions.class.php:
public function handleErrorLogin() { if (sfContext::getInstance()->getRequest()->getError('username') == 'cookies_disabled') { $this->redirect('@about_cookies'); } return sfView::SUCCESS; }
So that's it. Throw in an about cookies page and you're all set.
If I made any mistakes, I apologize. It's 5am on a school night.
If you're building a site with a user login system (like Askeet) and your PHP is configured to store session variables in a client-side cookie, the following snippet will improve the usability for users who have disabled cookies.
The following example assumes you already have at least a simple user login system on your site. If not, check out the Askeet tutorial for a great example to get you started.
All users who have disabled cookies will be unable to log into any site that relies on client-side cookies to store session variables. If you don't validate cookies and provide notification, these users will never know why they couldn't log in to your site.
Try the following if you'd like to see this firsthand.
Unfortunately, Askeet also provides the perfect example here as well. (Sorry guys!)
Not much happened, right? You're not logged in and you don't know why. (Well, you do now.)
What we're going to do is augment an existing login system to provide users with notification that your site requires cookies.
To do this, we're going to attempt to set a cookie on the login page and verify that it was created once the login form is submitted. Since a cookie is stored only after the page is loaded, it takes two pages to validate the user's setting. Fortunately, the login process takes two pages!
Edit your existing login action code by adding the following two setCookie() functions.
The first setCookie() sets the validation cookie when the login action is first loaded. (If you use your login on several pages as a component/partial, move this first setCookie() to the login form component.)
The second setCookie() deletes the validation cookie upon successful login. If this cookie is not deleted, the validation could return a false positive if the user disables cookies at a later date. (When cookies are disabled, new cookies can't be written, but old cookies can be read.)
module_dir/actions/actions.class.php:
public function executeLogin() { if ($this->getRequest()->getMethod() != sfRequest::POST) { sfContext::getInstance()->getResponse()->setCookie('cookies_enabled', 'true', time()+60*60*24, '/'); } else { sfContext::getInstance()->getResponse()->setCookie('cookies_enabled', '', 0, '/'); $this->redirect('@homepage'); } }
Enable cookie validation by adding the following code to your existing login validation. The validator takes two parameters, cookie_name and cookie_error. The cookie_name parameter is, oddly enough, the name of the cookie we'll use to validate and it should match the cookie set in the above code.
The other parameter, cookie_error, is the error message that will be returned if the user has cookies disabled. Typically, validation errors are phrases like "Invalid username" or "Password must contain 6-8 characters". But we're going to use "cookies_disabled" and I'll show you why in a few minutes.
IMPORTANT: The cookie validation should occur first.
module_dir/validate/login.yml:
methods: post: [username] names: username: required: true required_msg: validators: [cookiesEnabledValidator, userValidator] cookiesEnabledValidator: class: myCookiesEnabledValidator param: cookie_name: cookies_enabled cookie_error: cookies_disabled userValidator: class: myLoginValidator param: password: password username_error: Invalid username. password_error: Invalid password.
Copy the following code to one of your lib directories. Since it only deals with the login action, I choose to keep it in my user module's lib directory.
module_dir/lib/myCookiesEnabledValidator.class.php:
class myCookiesEnabledValidator extends sfValidator { public function initialize($context, $parameters = null) { // initialize parent parent::initialize($context); // set defaults $this->setParameter('cookie_name', sfContext::getInstance()->getStorage()->getParameter('session_name')); $this->setParameter('cookie_error', 'This site requires cookies.'); $this->getParameterHolder()->add($parameters); return true; } public function execute(&$value, &$error) { if (sfContext::getInstance()->getRequest()->getCookie($this->getParameter('cookie_name')) === null) { $error = $this->getParameter('cookie_error'); return false; } return true; } }
Now, since it takes two pages to set and read a cookie it wouldn't make sense to return the user to the form right away. If they enable their cookies they'll still have to submit the form twice before they'll login successfully. I prefer to send them to a page which notifies them that the site requires cookies and explains how they can enable them. (Plus, this gives us the extra click we need to set that validation cookie!)
So we'll check for the cookie validation error before returning to the form so we can redirect the user to our help page if necessary. The error we check for in the username parameter must match the cookie_error we defined in the login.yml.
module_dir/actions/actions.class.php:
public function handleErrorLogin() { if (sfContext::getInstance()->getRequest()->getError('username') == 'cookies_disabled') { $this->redirect('@about_cookies'); } return sfView::SUCCESS; }
So that's it. Throw in an about cookies page and you're all set.
If I made any mistakes, I apologize. It's 5am on a school night.
This is an easier method of setting variables for the session cookie. It available in versions above v0.6.3 (1.0 betas and higher). Examples assume your front application name is called "frontend".
config/frontend/factories.yml:
storage:
class: sfSessionStorage
param:
session_name: MYAPP_SESSION
session_cookie_lifetime: 77760000 # 90*24*3600
session_cookie_path: /
# session_cookie_domain: localhost
# session_cookie_secure: true
Comment/Uncomment what you need. That is all, there is no need to create a custom class. You may need to clear symfony's cache.
Recently I needed to copy data that comes from a webform into the current session. Here is my solution ...
class myUser extends sfBasicSecurityUser { /** * Copy request data into the users session object * * @param array $exclude Fieldnames to exclude from copy */ public function copyRequestData ($exclude = array ()) { /// Get the request instance and its currently posted fieldnames $req = sfContext::getInstance ()->getRequest (); $fieldNames = $req->getParameterHolder ()->getNames (); /// Always exclude "posted" module and action $exclude[] = 'module'; $exclude[] = 'action'; /// Evaluate each fieldname and do a "lookup" if it can /// be copied foreach ($fieldNames as $fieldName) { if (!in_array ($fieldName, $exclude)) { $this->setAttribute ($fieldName, $req->getParameter ($fieldName, null)); } // end if } // end foreach } }
I don't know about you, but I often farm out action logic--like building a datastructure to pass to a template--to static functions in a class in my application or module lib/ directory.
However, these functions don't typically have access to the shortcut methods for setting/getting flash and request parameters (etc.). Like:
$foo = $this->getRequestParameter('foo');
Unless I've missed something, the code to do something similar outside of an action is just a little messy, and quite repetitive if used more than once. So I add a few things to my project to make the job simpler.
/** * This class adds a few more useful functions to the sfUser instance */ class myBasicUser extends sfBasicSecurityUser { const FLASH_NAMESPACE = 'symfony/flash'; /** User flash parameters (stored in the session until the next request) */ public function setFlash($name, $value) { $this->setAttribute($name, $value, self::FLASH_NAMESPACE); } public function getFlash($name, $default = null) { return $this->getAttribute($name, $default, self::FLASH_NAMESPACE); } public function hasFlash($name) { return $this->hasAttribute($name, self::FLASH_NAMESPACE); } /** User attribute parameters (stored in the session until removed) */ public function removeAttribute($name, $ns = null) { $this->getAttributeHolder()->remove($name, $ns); } public function getAttributes($ns = null) { return $this->getAttributeHolder()->getAll($ns); } public function removeAttributes($ns = null) { $this->getAttributeHolder()->removeNamespace($ns); } /** User parameter parameters (erased after every request) */ public function removeParameter($name, $ns = null) { $this->getParameterHolder()->remove($name, $ns); } public function getParameters($ns = null) { return $this->getParameterHolder()->getAll($ns); } public function removeParameters($ns = null) { $this->getParameterHolder()->removeNamespace($ns); } } class myUser extends myBasicUser { //... }
*Note: add those files to your lib/ director as myBasicUser.class.php and myUser.class.php respectively.
That gives me an easy way to do some things with the user object, but now I need an easy way to get the user outside of an action and an easier way to get/set request parameters/attributes, etc. I defined a class in my application lib/ directory like this:
/** * Shortcuts to all request and user attribute setting/getting/has functions * This class simply makes repetitive tasks a few characters (and in some cases, lines) shorter to accomplish */ class myTools { /** Get context singleton */ public static function getContext() { return sfContext::getInstance(); } /** Get request singleton */ public static function getRequest() { return sfContext::getInstance()->getRequest(); } /** Request parameter holder */ public static function getRequestParameterHolder() { return sfContext::getInstance()->getRequest()->getParameterHolder(); } /** Request attribute holder */ public static function getRequestAttributeHolder() { return sfContext::getInstance()->getRequest()->getAttributeHolder(); } /** Request parameters (read-only) */ public static function getRequestParameter($name, $default = null) { return sfContext::getInstance()->getRequest()->getParameter($name, $default); } public static function hasRequestParameter($name) { return sfContext::getInstance()->getRequest()->hasParameter($name); } /** Request attributes (read/write - erased after every request) */ public static function getRequestAttribute($name, $default = null) { return sfContext::getInstance()->getRequest()->getAttribute($name, $default); } public static function setRequestAttribute($name, $value) { sfContext::getInstance()->getRequest()->setAttribute($name, $value); } public static function hasRequestAttribute($name) { return sfContext::getInstance()->getRequest()->hasAttribute($name); } /** Get user singleton */ public static function getUser() { return sfContext::getInstance()->getUser(); } /** User attribute holder */ public static function getAttributeHolder() { return sfContext::getInstance()->getUser()->getAttributeHolder(); } /** User parameter holder */ public static function getParameterHolder() { return sfContext::getInstance()->getUser()->getParameterHolder(); } /** User flash parameters (stored in the session until the next request) */ public static function setFlash($name, $value) { sfContext::getInstance()->getUser()->setFlash($name, $value); } public static function getFlash($name) { return sfContext::getInstance()->getUser()->getFlash($name); } public static function hasFlash($name) { return sfContext::getInstance()->getUser()->hasFlash($name); } /** User attribute parameters (stored in the session until removed) */ public static function getAttribute($name, $default = null, $ns = null) { return sfContext::getInstance()->getUser()->getAttribute($name, $default, $ns); } public static function setAttribute($name, $value, $ns = null) { sfContext::getInstance()->getUser()->setAttribute($name, $value, $ns); } public static function removeAttribute($name, $ns = null) { sfContext::getInstance()->getUser()->removeAttribute($name, $ns); } public static function getAttributes($ns = null) { return sfContext::getInstance()->getUser()->getAttributes($ns); } public static function removeAttributes($ns = null) { sfContext::getInstance()->getUser()->removeAttributes($ns); } /** User parameter parameters (erased after every request) */ public static function getParameter($name, $default = null, $ns = null) { return sfContext::getInstance()->getUser()->getParameter($name, $default, $ns); } public static function setParameter($name, $value, $ns = null) { sfContext::getInstance()->getUser()->setParameter($name, $value, $ns); } public static function removeParameter($name, $ns = null) { sfContext::getInstance()->getUser()->removeParameter($name, $ns); } public static function getParameters($ns = null) { return sfContext::getInstance()->getUser()->getParameters($ns); } public static function removeParameters($ns = null) { sfContext::getInstance()->getUser()->removeParameters($ns); } /** User credentials */ public static function clearCredentials() { sfContext::getInstance()->getUser()->clearCredentials(); } public static function listCredentials() { return sfContext::getInstance()->getUser()->listCredentials(); } public static function removeCredential($credential) { sfContext::getInstance()->getUser()->removeCredential($credential); } public static function addCredential($credential) { sfContext::getInstance()->getUser()->addCredential($credential); } public static function addCredentials() { sfContext::getInstance()->getUser()->addCredentials(func_get_args()); } public static function hasCredential($credential) { return sfContext::getInstance()->getUser()->hasCredential($credential); } /** User authentication */ public static function isAuthenticated() { return sfContext::getInstance()->getUser()->isAuthenticated(); } public static function setAuthenticated($authenticated) { sfContext::getInstance()->getUser()->setAuthenticated($authenticated); } /** User culture */ public static function setCulture($culture) { sfContext::getInstance()->getUser()->setCulture($culture); } public static function getCulture() { return sfContext::getInstance()->getUser()->getCulture(); } }
There you have it. Now it's as simple as...
class myModuleLib { public static function myFunc() { ... $foo = myTools::getRequestParameter('foo', 'default_value'); myTools::setFlash($foo); ... } }
Overkill...? Maybe. You may use any of these functions that are useful, and simply omit those you think are unnecessary. The goal here is to standardize (even more) the methods used to access attributes of the request and user, etc. I'm guessing this will be beneficial for beginners (if nothing else, as an example), and also for people (like me) who like ridiculously consistent code.
Any comments, including comments on naming conventions of the functions are appreciated.
This snippet is usefull to handle a session timeout in an ajax request.
It is very bad to have the login page filling the update div zone...
The idea came from a post in the forum (thanks a lot RoVeRT !)
The method is :
The security module redirect the request to the login action.
The login action send a 401 http error code if it detects an ajax request.
The ajax helper handle the 401 error with a javascript function which display a popup and redirect to the full login page.
For that purpose, we will add a little code in the ajax helper and in the login action.
1 - We handle the 401 error code in the ajax helper and enable javascript execution for the popup :
401 => "if ( confirm('Your not logged anymore... Ok to go to the login page.')) {document.location='/';}",
2 - We add this code at the beginning of the login action :
// if the request is an ajax request... if ($this->getRequest()->isXmlHttpRequest()) { // response to the ajax request : code http 401 (access unauthorized) $this->getResponse()->setStatusCode(401); }
Thanks for the comments !
Here is the schema you need to set up database session storage.
CREATE TABLE `session` ( `sess_id` varchar(32) NOT NULL, `sess_data` text NOT NULL, `sess_time` int(11) NOT NULL );
<?php class mySessionStorage extends sfMySQLSessionStorage { public function initialize ($context, $parameters = null) { session_set_cookie_params ( 90*24*3600 , "/", ".domain.tld" ); parent::initialize($context, $parameters); } } ?>
Then you have to put in factories.yml
all:
storage:
class: mySessionStorage
param:
database: ...
db_table: session
db_id_col: sess_id
db_data_col: sess_data
db_time_col: sess_time
session_name: ...
By default, symfony stores user sessions in files.
You can store them in your database by changing your apps/APPNAME/config/factories.yml configuration file:
all:
storage:
class: sfMySQLSessionStorage
param:
database: propel
db_table: SESSION_TABLE_NAME
There are several available session storage classes:
The API documentation lists all available configuration parameters.