$this->redirect('office/edit?id='.$office->getId());
}
}
+
+ public function executeLink(sfWebRequest $request)
+ {
+ $this->forward404Unless($office = Doctrine_Core::getTable('Office')->find(array($request->getParameter('id'))), sprintf('Object office does not exist (%s).', $request->getParameter('id')));
+
+ //Get user Id
+ $userId = $this->getUser()->getGuardUser()->getId();
+
+ //Get company owned by that user and insert value in form
+ $companyUserId = CompanyTable::getInstance()->findOneByUserId($userId)->getId();
+
+ //Get id number sent by the user (never trust the users)
+ $officeId = $request->getParameter('id');
+
+ $companyOfficeId = $office->getCompanyId();
+
+ $this->forward404Unless($companyOfficeId == $companyUserId, sprintf('Office does not exist (%s).', $request->getParameter('id')));
+
+ $officeAds = OfficeAdsTable::getInstance()->findOneByOfficeId($officeId);
+
+ $this->form = new OfficeAdsForm($officeAds, array('companyId' => $companyOfficeId));
+ }
}
--- /dev/null
+<?php use_stylesheets_for_form($form) ?>
+<?php use_javascripts_for_form($form) ?>
+<?php use_javascript('/sfFormExtraPlugin/js/double_list.js') ?>
+
+<form action="<?php echo url_for('office/'.($form->getObject()->isNew() ? 'create' : 'update').(!$form->getObject()->isNew() ? '?id='.$form->getObject()->getId() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>>
+<?php if (!$form->getObject()->isNew()): ?>
+<input type="hidden" name="sf_method" value="put" />
+<?php endif; ?>
+ <table>
+ <tfoot>
+ <tr>
+ <td colspan="2">
+ <?php echo $form->renderHiddenFields(false) ?>
+ <a href="<?php echo url_for('office/index') ?>"><?php echo __('Back to list') ?></a>
+ <input type="submit" value=<?php echo __('Save') ?> />
+ </td>
+ </tr>
+ </tfoot>
+ <tbody>
+ <?php echo $form ?>
+ </tbody>
+ </table>
+</form>
<td><?php echo $office->getLongitude() ?></td>
<td><?php echo $office->getLatitude() ?></td>
<td><a href="<?php echo url_for('office/edit?id='.$office->getId()) ?>"><img src="/images/pencil_add.png" alt="" title="" border="0" /></a></td>
- <td><a><img src="/images/link.png" alt="" title="" border="0" /></a></td>
+ <td><a href="<?php echo url_for('office/link?id='.$office->getId()) ?>"><img src="/images/link.png" alt="" title="" border="0" /></a></td>
<td><?php echo link_to('<img src="/images/inadminpanel/images/trash.png" alt="" title="" border="0" />', 'office/delete?id='.$office->getId(), array('method' => 'delete', 'confirm' => 'Are you sure?')) ?></td>
</tr>
--- /dev/null
+<h2><?php echo __('Link to Ads') ?></h2>
+
+<?php include_partial('formCustomOfficeAds', array('form' => $form)) ?>
{
$this->enablePlugins('sfDoctrinePlugin');
$this->enablePlugins('sfDoctrineGuardPlugin');
+ $this->enablePlugins('sfFormExtraPlugin');
}
}
*
* @package mobiads
* @subpackage form
- * @author Your name here
- * @version SVN: $Id: sfDoctrineFormTemplate.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $
+ * @author Gustavo Martin Morcuende
+ * @version
*/
class OfficeAdsForm extends BaseOfficeAdsForm
{
public function configure()
{
+ //Narrow down options.
+ //We must just show those ads owned by the office's company.
+ $query = AdTable::getInstance()->getAdsByCompanyIdQuery($this->getOption('companyId'));
+
+ $this->useFields(array('ad_id'));
+
+ $this->widgetSchema['ad_id'] = new sfWidgetFormDoctrineChoice(array('model' => $this->getRelatedModelName('Ad'),
+ 'add_empty' => false,
+ 'multiple' => true,
+ 'expanded' => false,
+ 'renderer_class' => 'sfWidgetFormSelectDoubleList',
+ 'query' => $query));
+
+ $this->validatorSchema['ad_id'] = new sfValidatorDoctrineChoice(array('model' => $this->getRelatedModelName('Ad'),
+ 'multiple' => true,
+ 'query' => $query));
+ }
+
+ /**
+ * Overriding updateDefaultsFromObject method in order to preselect fields.
+ *
+ * The already chosen ads must be shown as selected.
+ * see: lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/form/sfFormDoctrine.class.php
+ */
+ protected function updateDefaultsFromObject()
+ {
+ parent::updateDefaultsFromObject();
+
+ //We just preselect fields when the Doctrine Record Object comes from the data base.
+ if (!$this->getObject()->isNew())
+ {
+ $officeAds = OfficeAdsTable::getInstance()->findByOfficeId($this->getObject()->getOfficeId());
+ foreach ($officeAds as $officeAd)
+ {
+ $already_chosen[] = $officeAd->getAdId();
+ }
+ $this->setDefault('ad_id', $already_chosen);
+ }
}
}
{
return Doctrine_Core::getTable('Ad');
}
-}
\ No newline at end of file
+
+
+ /**
+ * Returns ads by company id.
+ *
+ * @return related ads to a company id as Doctrine Query
+ */
+ public function getAdsByCompanyIdQuery($companyId)
+ {
+ return $this->createQuery('ad')->where('ad.company_id = ?', $companyId);
+ }
+}
{
return Doctrine_Core::getTable('OfficeAds');
}
-}
\ No newline at end of file
+}
-a:1:{s:4:"data";a:86:{s:48:"sfdoctrineguardplugin/config/doctrine/schema.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/config/sfDoctrineGuardPluginConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:55:"sfdoctrineguardplugin/data/fixtures/fixtures.yml.sample";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:36:"sfdoctrineguardplugin/data/tasks/.sf";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:42:"sfdoctrineguardplugin/i18n/sf_guard.es.xml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:42:"sfdoctrineguardplugin/i18n/sf_guard.fr.xml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:80:"sfdoctrineguardplugin/lib/filter/doctrine/PluginsfGuardGroupFormFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:85:"sfdoctrineguardplugin/lib/filter/doctrine/PluginsfGuardPermissionFormFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/filter/doctrine/PluginsfGuardUserFormFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:82:"sfdoctrineguardplugin/lib/form/base/BasesfGuardRequestForgotPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:88:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardChangeUserPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:76:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardFormSignin.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardRegisterForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardUserAdminForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:72:"sfdoctrineguardplugin/lib/form/doctrine/PluginsfGuardGroupForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:77:"sfdoctrineguardplugin/lib/form/doctrine/PluginsfGuardPermissionForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:71:"sfdoctrineguardplugin/lib/form/doctrine/PluginsfGuardUserForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardChangeUserPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:67:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardFormSignin.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardRegisterForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:70:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardUserAdminForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/lib/form/sfGuardRequestForgotPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardForgotPassword.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:83:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardForgotPasswordTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroup.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroupPermission.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:84:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroupPermissionTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:74:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroupTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:74:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardPermission.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardPermissionTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:75:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardRememberKey.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:80:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardRememberKeyTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUser.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserGroup.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserGroupTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserPermission.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:83:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserPermissionTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:58:"sfdoctrineguardplugin/lib/routing/sfGuardRouting.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:60:"sfdoctrineguardplugin/lib/task/sfGuardAddGroupTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:65:"sfdoctrineguardplugin/lib/task/sfGuardAddPermissionTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:66:"sfdoctrineguardplugin/lib/task/sfGuardChangePasswordTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:62:"sfdoctrineguardplugin/lib/task/sfGuardCreateUserTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/lib/task/sfGuardPromoteSuperAdminTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:60:"sfdoctrineguardplugin/lib/user/sfGuardSecurityUser.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:66:"sfdoctrineguardplugin/lib/validator/sfGuardValidatorUser.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:62:"sfdoctrineguardplugin/lib/sfGuardBasicSecurityFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:59:"sfdoctrineguardplugin/lib/sfGuardRememberMeFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:67:"sfdoctrineguardplugin/modules/sfGuardAuth/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:70:"sfdoctrineguardplugin/modules/sfGuardAuth/actions/components.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:61:"sfdoctrineguardplugin/modules/sfGuardAuth/config/security.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/modules/sfGuardAuth/lib/BasesfGuardAuthActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:81:"sfdoctrineguardplugin/modules/sfGuardAuth/lib/BasesfGuardAuthComponents.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/modules/sfGuardAuth/templates/_signin_form.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/modules/sfGuardAuth/templates/secureSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/modules/sfGuardAuth/templates/signinSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:77:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:98:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/lib/BasesfGuardForgotPasswordActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/_new_password.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/_send_request.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/changeSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/indexSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/modules/sfGuardGroup/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:63:"sfdoctrineguardplugin/modules/sfGuardGroup/config/generator.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:91:"sfdoctrineguardplugin/modules/sfGuardGroup/lib/sfGuardGroupGeneratorConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:84:"sfdoctrineguardplugin/modules/sfGuardGroup/lib/sfGuardGroupGeneratorHelper.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/modules/sfGuardPermission/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/modules/sfGuardPermission/config/generator.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:101:"sfdoctrineguardplugin/modules/sfGuardPermission/lib/sfGuardPermissionGeneratorConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:94:"sfdoctrineguardplugin/modules/sfGuardPermission/lib/sfGuardPermissionGeneratorHelper.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:71:"sfdoctrineguardplugin/modules/sfGuardRegister/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:74:"sfdoctrineguardplugin/modules/sfGuardRegister/actions/components.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:86:"sfdoctrineguardplugin/modules/sfGuardRegister/lib/BasesfGuardRegisterActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:89:"sfdoctrineguardplugin/modules/sfGuardRegister/lib/BasesfGuardRegisterComponents.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:65:"sfdoctrineguardplugin/modules/sfGuardRegister/templates/_form.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:72:"sfdoctrineguardplugin/modules/sfGuardRegister/templates/indexSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:67:"sfdoctrineguardplugin/modules/sfGuardUser/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:62:"sfdoctrineguardplugin/modules/sfGuardUser/config/generator.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/modules/sfGuardUser/lib/BasesfGuardUserActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:89:"sfdoctrineguardplugin/modules/sfGuardUser/lib/sfGuardUserGeneratorConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:82:"sfdoctrineguardplugin/modules/sfGuardUser/lib/sfGuardUserGeneratorHelper.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:31:"sfdoctrineguardplugin/.DS_Store";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:29:"sfdoctrineguardplugin/LICENSE";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:28:"sfdoctrineguardplugin/README";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:53:"sfdoctrineguardplugin/sfDoctrineGuardPlugin-5.0.0.tgz";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:29:"sfdoctrineguardplugin/VERSION";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}}}
\ No newline at end of file
+a:1:{s:4:"data";a:118:{s:48:"sfdoctrineguardplugin/config/doctrine/schema.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/config/sfDoctrineGuardPluginConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:55:"sfdoctrineguardplugin/data/fixtures/fixtures.yml.sample";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:36:"sfdoctrineguardplugin/data/tasks/.sf";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:42:"sfdoctrineguardplugin/i18n/sf_guard.es.xml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:42:"sfdoctrineguardplugin/i18n/sf_guard.fr.xml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:80:"sfdoctrineguardplugin/lib/filter/doctrine/PluginsfGuardGroupFormFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:85:"sfdoctrineguardplugin/lib/filter/doctrine/PluginsfGuardPermissionFormFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/filter/doctrine/PluginsfGuardUserFormFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:82:"sfdoctrineguardplugin/lib/form/base/BasesfGuardRequestForgotPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:88:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardChangeUserPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:76:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardFormSignin.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardRegisterForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/form/doctrine/base/BasesfGuardUserAdminForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:72:"sfdoctrineguardplugin/lib/form/doctrine/PluginsfGuardGroupForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:77:"sfdoctrineguardplugin/lib/form/doctrine/PluginsfGuardPermissionForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:71:"sfdoctrineguardplugin/lib/form/doctrine/PluginsfGuardUserForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardChangeUserPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:67:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardFormSignin.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardRegisterForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:70:"sfdoctrineguardplugin/lib/form/doctrine/sfGuardUserAdminForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/lib/form/sfGuardRequestForgotPasswordForm.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardForgotPassword.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:83:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardForgotPasswordTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroup.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroupPermission.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:84:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroupPermissionTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:74:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardGroupTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:74:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardPermission.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardPermissionTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:75:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardRememberKey.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:80:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardRememberKeyTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUser.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserGroup.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserGroupTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserPermission.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:83:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserPermissionTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/lib/model/doctrine/PluginsfGuardUserTable.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:58:"sfdoctrineguardplugin/lib/routing/sfGuardRouting.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:60:"sfdoctrineguardplugin/lib/task/sfGuardAddGroupTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:65:"sfdoctrineguardplugin/lib/task/sfGuardAddPermissionTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:66:"sfdoctrineguardplugin/lib/task/sfGuardChangePasswordTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:62:"sfdoctrineguardplugin/lib/task/sfGuardCreateUserTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/lib/task/sfGuardPromoteSuperAdminTask.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:60:"sfdoctrineguardplugin/lib/user/sfGuardSecurityUser.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:66:"sfdoctrineguardplugin/lib/validator/sfGuardValidatorUser.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:62:"sfdoctrineguardplugin/lib/sfGuardBasicSecurityFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:59:"sfdoctrineguardplugin/lib/sfGuardRememberMeFilter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:67:"sfdoctrineguardplugin/modules/sfGuardAuth/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:70:"sfdoctrineguardplugin/modules/sfGuardAuth/actions/components.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:61:"sfdoctrineguardplugin/modules/sfGuardAuth/config/security.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/modules/sfGuardAuth/lib/BasesfGuardAuthActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:81:"sfdoctrineguardplugin/modules/sfGuardAuth/lib/BasesfGuardAuthComponents.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/modules/sfGuardAuth/templates/_signin_form.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/modules/sfGuardAuth/templates/secureSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:69:"sfdoctrineguardplugin/modules/sfGuardAuth/templates/signinSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:77:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:98:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/lib/BasesfGuardForgotPasswordActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/_new_password.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/_send_request.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:79:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/changeSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/modules/sfGuardForgotPassword/templates/indexSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/modules/sfGuardGroup/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:63:"sfdoctrineguardplugin/modules/sfGuardGroup/config/generator.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:91:"sfdoctrineguardplugin/modules/sfGuardGroup/lib/sfGuardGroupGeneratorConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:84:"sfdoctrineguardplugin/modules/sfGuardGroup/lib/sfGuardGroupGeneratorHelper.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:73:"sfdoctrineguardplugin/modules/sfGuardPermission/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:68:"sfdoctrineguardplugin/modules/sfGuardPermission/config/generator.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:101:"sfdoctrineguardplugin/modules/sfGuardPermission/lib/sfGuardPermissionGeneratorConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:94:"sfdoctrineguardplugin/modules/sfGuardPermission/lib/sfGuardPermissionGeneratorHelper.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:71:"sfdoctrineguardplugin/modules/sfGuardRegister/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:74:"sfdoctrineguardplugin/modules/sfGuardRegister/actions/components.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:86:"sfdoctrineguardplugin/modules/sfGuardRegister/lib/BasesfGuardRegisterActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:89:"sfdoctrineguardplugin/modules/sfGuardRegister/lib/BasesfGuardRegisterComponents.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:65:"sfdoctrineguardplugin/modules/sfGuardRegister/templates/_form.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:72:"sfdoctrineguardplugin/modules/sfGuardRegister/templates/indexSuccess.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:67:"sfdoctrineguardplugin/modules/sfGuardUser/actions/actions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:62:"sfdoctrineguardplugin/modules/sfGuardUser/config/generator.yml";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:78:"sfdoctrineguardplugin/modules/sfGuardUser/lib/BasesfGuardUserActions.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:89:"sfdoctrineguardplugin/modules/sfGuardUser/lib/sfGuardUserGeneratorConfiguration.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:82:"sfdoctrineguardplugin/modules/sfGuardUser/lib/sfGuardUserGeneratorHelper.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:31:"sfdoctrineguardplugin/.DS_Store";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:29:"sfdoctrineguardplugin/LICENSE";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:28:"sfdoctrineguardplugin/README";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:53:"sfdoctrineguardplugin/sfDoctrineGuardPlugin-5.0.0.tgz";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:29:"sfdoctrineguardplugin/VERSION";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:21:"sfdoctrineguardplugin";}s:31:"sfformextraplugin/bin/prove.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:51:"sfformextraplugin/lib/form/sfFormLanguage.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:84:"sfformextraplugin/lib/validator/doctrine/sfValidatorDoctrineNestedSetLevel.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:62:"sfformextraplugin/lib/validator/sfValidatorBlacklist.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:62:"sfformextraplugin/lib/validator/sfValidatorReCaptcha.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:71:"sfformextraplugin/lib/validator/sfValidatorSchemaTimeInterval.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:60:"sfformextraplugin/lib/validator/sfValidatorDefault.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:72:"sfformextraplugin/lib/widget/sfWidgetFormDoctrineChoiceGrouped.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:78:"sfformextraplugin/lib/widget/sfWidgetFormDoctrineJQueryAutocompleter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:70:"sfformextraplugin/lib/widget/sfWidgetFormJQueryAutocompleter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:61:"sfformextraplugin/lib/widget/sfWidgetFormJQueryDate.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:70:"sfformextraplugin/lib/widget/sfWidgetFormPropelChoiceGrouped.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:76:"sfformextraplugin/lib/widget/sfWidgetFormPropelJQueryAutocompleter.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:60:"sfformextraplugin/lib/widget/sfWidgetFormReCaptcha.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:67:"sfformextraplugin/lib/widget/sfWidgetFormSelectDoubleList.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:64:"sfformextraplugin/lib/widget/sfWidgetFormSelectUSState.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:66:"sfformextraplugin/lib/widget/sfWidgetFormTextareaTinyMCE.class.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:50:"sfformextraplugin/test/form/sfFormLanguageTest.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:61:"sfformextraplugin/test/validator/sfValidatorReCaptchaTest.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:61:"sfformextraplugin/test/validator/sfValidatorBlacklistTest.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:70:"sfformextraplugin/test/validator/sfValidatorSchemaTimeIntervalTest.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:59:"sfformextraplugin/test/validator/sfValidatorDefaultTest.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:59:"sfformextraplugin/test/widget/sfWidgetFormReCaptchaTest.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:36:"sfformextraplugin/test/bootstrap.php";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:50:"sfformextraplugin/web/css/jquery.autocompleter.css";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:39:"sfformextraplugin/web/js/double_list.js";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:48:"sfformextraplugin/web/js/jquery.autocompleter.js";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:42:"sfformextraplugin/web/images/indicator.gif";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:37:"sfformextraplugin/web/images/next.png";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:41:"sfformextraplugin/web/images/previous.png";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:24:"sfformextraplugin/README";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}s:25:"sfformextraplugin/LICENSE";a:2:{i:0;s:27:"plugins.symfony-project.org";i:1;s:17:"sfformextraplugin";}}}
\ No newline at end of file
a:18:{s:4:"name";s:7:"symfony";s:7:"attribs";a:5:{s:7:"version";s:3:"2.0";s:5:"xmlns";s:35:"http://pear.php.net/dtd/package-2.0";s:11:"xmlns:tasks";s:33:"http://pear.php.net/dtd/tasks-1.0";s:9:"xmlns:xsi";s:41:"http://www.w3.org/2001/XMLSchema-instance";s:18:"xsi:schemaLocation";s:159:"http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
- http://pear.php.net/dtd/package-2.0.xsd";}s:7:"channel";s:24:"pear.symfony-project.com";s:7:"summary";s:7:"symfony";s:11:"description";s:7:"symfony";s:4:"lead";a:4:{s:4:"name";s:16:"Fabien Potencier";s:4:"user";s:6:"fabpot";s:5:"email";s:36:"fabien.potencier@symfony-project.com";s:6:"active";s:3:"yes";}s:4:"date";s:10:"2012-05-10";s:7:"version";a:2:{s:7:"release";s:6:"1.4.18";s:3:"api";s:5:"1.4.0";}s:9:"stability";a:2:{s:7:"release";s:4:"beta";s:3:"api";s:4:"beta";}s:7:"license";s:11:"MIT License";s:5:"notes";s:1:"-";s:8:"contents";a:1:{s:3:"dir";a:1:{s:7:"attribs";a:1:{s:4:"name";s:1:"/";}}}s:12:"dependencies";a:1:{s:8:"required";a:2:{s:3:"php";a:1:{s:3:"min";s:5:"5.2.4";}s:13:"pearinstaller";a:1:{s:3:"min";s:5:"1.4.3";}}}s:10:"phprelease";a:0:{}s:8:"filelist";a:0:{}s:3:"old";a:7:{s:7:"version";s:6:"1.4.18";s:12:"release_date";s:10:"2012-05-10";s:13:"release_state";s:4:"beta";s:15:"release_license";s:11:"MIT License";s:13:"release_notes";s:1:"-";s:12:"release_deps";a:2:{i:0;a:4:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:5:"5.2.4";s:8:"optional";s:2:"no";}i:1;a:6:{s:4:"type";s:3:"pkg";s:7:"channel";s:12:"pear.php.net";s:4:"name";s:4:"PEAR";s:3:"rel";s:2:"ge";s:7:"version";s:5:"1.4.3";s:8:"optional";s:2:"no";}}s:11:"maintainers";a:1:{i:0;a:5:{s:4:"name";s:16:"Fabien Potencier";s:5:"email";s:36:"fabien.potencier@symfony-project.com";s:6:"active";s:3:"yes";s:6:"handle";s:6:"fabpot";s:4:"role";s:4:"lead";}}}s:10:"xsdversion";s:3:"2.0";s:13:"_lastmodified";i:1336675411;}
\ No newline at end of file
+ http://pear.php.net/dtd/package-2.0.xsd";}s:7:"channel";s:24:"pear.symfony-project.com";s:7:"summary";s:7:"symfony";s:11:"description";s:7:"symfony";s:4:"lead";a:4:{s:4:"name";s:16:"Fabien Potencier";s:4:"user";s:6:"fabpot";s:5:"email";s:36:"fabien.potencier@symfony-project.com";s:6:"active";s:3:"yes";}s:4:"date";s:10:"2012-05-17";s:7:"version";a:2:{s:7:"release";s:6:"1.4.18";s:3:"api";s:5:"1.4.0";}s:9:"stability";a:2:{s:7:"release";s:4:"beta";s:3:"api";s:4:"beta";}s:7:"license";s:11:"MIT License";s:5:"notes";s:1:"-";s:8:"contents";a:1:{s:3:"dir";a:1:{s:7:"attribs";a:1:{s:4:"name";s:1:"/";}}}s:12:"dependencies";a:1:{s:8:"required";a:2:{s:3:"php";a:1:{s:3:"min";s:5:"5.2.4";}s:13:"pearinstaller";a:1:{s:3:"min";s:5:"1.4.3";}}}s:10:"phprelease";a:0:{}s:8:"filelist";a:0:{}s:3:"old";a:7:{s:7:"version";s:6:"1.4.18";s:12:"release_date";s:10:"2012-05-17";s:13:"release_state";s:4:"beta";s:15:"release_license";s:11:"MIT License";s:13:"release_notes";s:1:"-";s:12:"release_deps";a:2:{i:0;a:4:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:5:"5.2.4";s:8:"optional";s:2:"no";}i:1;a:6:{s:4:"type";s:3:"pkg";s:7:"channel";s:12:"pear.php.net";s:4:"name";s:4:"PEAR";s:3:"rel";s:2:"ge";s:7:"version";s:5:"1.4.3";s:8:"optional";s:2:"no";}}s:11:"maintainers";a:1:{i:0;a:5:{s:4:"name";s:16:"Fabien Potencier";s:5:"email";s:36:"fabien.potencier@symfony-project.com";s:6:"active";s:3:"yes";s:6:"handle";s:6:"fabpot";s:4:"role";s:4:"lead";}}}s:10:"xsdversion";s:3:"2.0";s:13:"_lastmodified";i:1337219150;}
\ No newline at end of file
--- /dev/null
+a:23:{s:7:"attribs";a:6:{s:5:"xmlns";s:35:"http://pear.php.net/dtd/package-2.0";s:11:"xmlns:tasks";s:33:"http://pear.php.net/dtd/tasks-1.0";s:9:"xmlns:xsi";s:41:"http://www.w3.org/2001/XMLSchema-instance";s:15:"packagerversion";s:5:"1.9.1";s:7:"version";s:3:"2.0";s:18:"xsi:schemaLocation";s:147:"http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd";}s:4:"name";s:17:"sfFormExtraPlugin";s:7:"channel";s:27:"plugins.symfony-project.org";s:7:"summary";s:31:"Validators, Widgets, and Forms.";s:11:"description";s:31:"Validators, Widgets, and Forms.";s:4:"lead";a:4:{s:4:"name";s:16:"Fabien Potencier";s:4:"user";s:6:"fabpot";s:5:"email";s:36:"fabien.potencier@symfony-project.com";s:6:"active";s:3:"yes";}s:9:"developer";a:3:{i:0;a:4:{s:4:"name";s:17:"Nicolas Perriault";s:4:"user";s:7:"nicolas";s:5:"email";s:37:"nicolas.perriault@symfony-project.com";s:6:"active";s:3:"yes";}i:1;a:4:{s:4:"name";s:14:"Kris Wallsmith";s:4:"user";s:14:"Kris.Wallsmith";s:5:"email";s:34:"kris.wallsmith@symfony-project.com";s:6:"active";s:3:"yes";}i:2;a:4:{s:4:"name";s:10:"Hugo Hamon";s:4:"user";s:10:"hugo.hamon";s:5:"email";s:21:"hugo.hamon@sensio.com";s:6:"active";s:3:"yes";}}s:4:"date";s:10:"2010-08-25";s:4:"time";s:8:"14:00:37";s:7:"version";a:2:{s:7:"release";s:5:"1.1.3";s:3:"api";s:5:"1.0.0";}s:9:"stability";a:2:{s:7:"release";s:6:"stable";s:3:"api";s:6:"stable";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:38:"http://www.symfony-project.com/license";}s:8:"_content";s:11:"MIT license";}s:5:"notes";s:1:"-";s:8:"contents";a:1:{s:3:"dir";a:2:{s:7:"attribs";a:1:{s:4:"name";s:1:"/";}s:4:"file";a:32:{i:0;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"dd477c00b9182a8fce0a2931b2afe14f";s:4:"name";s:13:"bin/prove.php";s:4:"role";s:4:"data";}}i:1;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"352b4711bca96a9ef711c02ba51a7487";s:4:"name";s:33:"lib/form/sfFormLanguage.class.php";s:4:"role";s:4:"data";}}i:2;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"1374000a0198f4a174f80795e60e7c71";s:4:"name";s:66:"lib/validator/doctrine/sfValidatorDoctrineNestedSetLevel.class.php";s:4:"role";s:4:"data";}}i:3;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"47573deff2c7264ae1a31ead15940eed";s:4:"name";s:44:"lib/validator/sfValidatorBlacklist.class.php";s:4:"role";s:4:"data";}}i:4;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"71d741ed1a332e2c3c5e5eba43061123";s:4:"name";s:44:"lib/validator/sfValidatorReCaptcha.class.php";s:4:"role";s:4:"data";}}i:5;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"f1af2b598238238258888f18dbc8a257";s:4:"name";s:53:"lib/validator/sfValidatorSchemaTimeInterval.class.php";s:4:"role";s:4:"data";}}i:6;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"122f99df59284f63338fd9a1e055e4ff";s:4:"name";s:42:"lib/validator/sfValidatorDefault.class.php";s:4:"role";s:4:"data";}}i:7;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"b0a625150802410c9a39dee434746908";s:4:"name";s:54:"lib/widget/sfWidgetFormDoctrineChoiceGrouped.class.php";s:4:"role";s:4:"data";}}i:8;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"8d3f91f1b54429c94b71517bd3840e20";s:4:"name";s:60:"lib/widget/sfWidgetFormDoctrineJQueryAutocompleter.class.php";s:4:"role";s:4:"data";}}i:9;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"8ca49e7b709485623ebd124950a0a41f";s:4:"name";s:52:"lib/widget/sfWidgetFormJQueryAutocompleter.class.php";s:4:"role";s:4:"data";}}i:10;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"0a004f96c83c50872775bcc698d5dabb";s:4:"name";s:43:"lib/widget/sfWidgetFormJQueryDate.class.php";s:4:"role";s:4:"data";}}i:11;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"8c909201670d42f4d4ddfe46eab45adb";s:4:"name";s:52:"lib/widget/sfWidgetFormPropelChoiceGrouped.class.php";s:4:"role";s:4:"data";}}i:12;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"45adc6b7bfb2296d7bc77a83a441ac88";s:4:"name";s:58:"lib/widget/sfWidgetFormPropelJQueryAutocompleter.class.php";s:4:"role";s:4:"data";}}i:13;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"9feb211dbd9e4e274b7981f41af339cb";s:4:"name";s:42:"lib/widget/sfWidgetFormReCaptcha.class.php";s:4:"role";s:4:"data";}}i:14;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"83860c7eea89ef17f0e4d88268fbb7fc";s:4:"name";s:49:"lib/widget/sfWidgetFormSelectDoubleList.class.php";s:4:"role";s:4:"data";}}i:15;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"4b4bedb41a4adb487ecc39cc47be4eab";s:4:"name";s:46:"lib/widget/sfWidgetFormSelectUSState.class.php";s:4:"role";s:4:"data";}}i:16;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"b4e4b61fba61b26db3ac30bb5933771b";s:4:"name";s:48:"lib/widget/sfWidgetFormTextareaTinyMCE.class.php";s:4:"role";s:4:"data";}}i:17;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"ceecaca65fdf3b4c5e037ab6e696d06a";s:4:"name";s:32:"test/form/sfFormLanguageTest.php";s:4:"role";s:4:"data";}}i:18;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"6ea19158046962f6ad76776ed3ad8673";s:4:"name";s:43:"test/validator/sfValidatorReCaptchaTest.php";s:4:"role";s:4:"data";}}i:19;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"0c08b48fbc88fb98aa6062809cba54c8";s:4:"name";s:43:"test/validator/sfValidatorBlacklistTest.php";s:4:"role";s:4:"data";}}i:20;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"1f6fd7556f6f3a68b7a31ba859c4175b";s:4:"name";s:52:"test/validator/sfValidatorSchemaTimeIntervalTest.php";s:4:"role";s:4:"data";}}i:21;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"92b4a5838358108197afe3afe8aee679";s:4:"name";s:41:"test/validator/sfValidatorDefaultTest.php";s:4:"role";s:4:"data";}}i:22;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"993dc134aefab7538455472af3a3c5a4";s:4:"name";s:41:"test/widget/sfWidgetFormReCaptchaTest.php";s:4:"role";s:4:"data";}}i:23;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"e04220de43865dc7cf4cc7fe5965873b";s:4:"name";s:18:"test/bootstrap.php";s:4:"role";s:4:"data";}}i:24;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"88def8417a8aefc9fa5d833a4f4d0373";s:4:"name";s:32:"web/css/jquery.autocompleter.css";s:4:"role";s:4:"data";}}i:25;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"460d935f81fd9de0c01656fd4f294757";s:4:"name";s:21:"web/js/double_list.js";s:4:"role";s:4:"data";}}i:26;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"b4dc9ff8b1a5569405fe53a1d4dd4f4e";s:4:"name";s:30:"web/js/jquery.autocompleter.js";s:4:"role";s:4:"data";}}i:27;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"03ce3dcc84af110e9da8699a841e5200";s:4:"name";s:24:"web/images/indicator.gif";s:4:"role";s:4:"data";}}i:28;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"64113e3f418860c6c27caa83d8494c73";s:4:"name";s:19:"web/images/next.png";s:4:"role";s:4:"data";}}i:29;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"4485131fafbc7b6bd6018a67671e9040";s:4:"name";s:23:"web/images/previous.png";s:4:"role";s:4:"data";}}i:30;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"9c80350d278bfbcf24b946d86df8fab6";s:4:"name";s:6:"README";s:4:"role";s:4:"data";}}i:31;a:1:{s:7:"attribs";a:3:{s:6:"md5sum";s:32:"4db2c5c669396b6c794f2fe214cb33e8";s:4:"name";s:7:"LICENSE";s:4:"role";s:4:"data";}}}}}s:12:"dependencies";a:1:{s:8:"required";a:2:{s:3:"php";a:1:{s:3:"min";s:5:"5.2.4";}s:13:"pearinstaller";a:1:{s:3:"min";s:5:"1.4.1";}}}s:10:"phprelease";s:0:"";s:9:"changelog";a:1:{s:7:"release";a:4:{i:0;a:5:{s:7:"version";a:2:{s:7:"release";s:5:"1.1.3";s:3:"api";s:5:"1.0.0";}s:9:"stability";a:2:{s:7:"release";s:6:"stable";s:3:"api";s:6:"stable";}s:7:"license";a:2:{i:0;a:2:{s:7:"attribs";a:1:{s:3:"uri";s:38:"http://www.symfony-project.com/license";}s:8:"_content";s:11:"MIT license";}i:1;s:3:"MIT";}s:4:"date";s:10:"2010-08-25";s:5:"notes";s:575:"* xavier: allowed unassociated list to be left of the associated one (#7780)
+* Fabien: added culture and theme options for sfWidgetFormReCaptcha (#8509)
+* Fabien: updated the autocomplete JavaScript to the latest version
+* Fabien: fixed sfWidgetFormJQueryDate doesn't refresh "disabled" after the date was chosen from calendar (#8817)
+* Xavier: fixed sfWidgetFormJQueryDate doesn't restrict values on page load (#7792)
+* Fabien: fixed JS function names when the widget name contains - (#8836)
+* Fabien: added support for sfWidgetFormDateTime in sfWidgetFormJQueryDate (#8950)";}i:1;a:5:{s:7:"version";a:2:{s:7:"release";s:5:"1.1.2";s:3:"api";s:5:"1.0.0";}s:9:"stability";a:2:{s:7:"release";s:6:"stable";s:3:"api";s:6:"stable";}s:7:"license";a:2:{i:0;a:2:{s:7:"attribs";a:1:{s:3:"uri";s:38:"http://www.symfony-project.com/license";}s:8:"_content";s:11:"MIT license";}i:1;s:3:"MIT";}s:4:"date";s:10:"2010-08-03";s:5:"notes";s:314:"* Kris.Wallsmith: added sfValidatorDefault, which returns a default value rather than throw an error
+* Kris.Wallsmith: fixed blacklist validator test
+* FabianLange: fixed incorrect octal parsing of dates in javascript (#7743)
+* Kris.Wallsmith: fixed reading of can_be_empty option in sfWidgetFormJQueryDate (#8723)";}i:2;a:5:{s:7:"version";a:2:{s:7:"release";s:5:"1.1.1";s:3:"api";s:5:"1.0.0";}s:9:"stability";a:2:{s:7:"release";s:6:"stable";s:3:"api";s:6:"stable";}s:7:"license";a:2:{i:0;a:2:{s:7:"attribs";a:1:{s:3:"uri";s:38:"http://www.symfony-project.com/license";}s:8:"_content";s:11:"MIT license";}i:1;s:3:"MIT";}s:4:"date";s:10:"2009-11-30";s:5:"notes";s:232:"* fabien: added a date_widget option to sfWidgetFormJQueryDate (#5382)
+* fabien: changed the sfFormLanguage to take the current user culture into account (#6752)
+* fabien: added sfWidgetFormDoctrineJQueryAutocompleter (#5118, #4943)";}i:3;a:5:{s:7:"version";a:2:{s:7:"release";s:5:"1.1.0";s:3:"api";s:5:"1.0.0";}s:9:"stability";a:2:{s:7:"release";s:6:"stable";s:3:"api";s:6:"stable";}s:7:"license";a:2:{i:0;a:2:{s:7:"attribs";a:1:{s:3:"uri";s:38:"http://www.symfony-project.com/license";}s:8:"_content";s:11:"MIT license";}i:1;s:3:"MIT";}s:4:"date";s:10:"2009-11-30";s:5:"notes";s:50:"* fabien: made the plugin work for symfony 1.3/1.4";}}}s:8:"filelist";a:32:{s:13:"bin/prove.php";a:4:{s:6:"md5sum";s:32:"dd477c00b9182a8fce0a2931b2afe14f";s:4:"name";s:13:"bin/prove.php";s:4:"role";s:4:"data";s:12:"installed_as";s:77:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/bin/prove.php";}s:33:"lib/form/sfFormLanguage.class.php";a:4:{s:6:"md5sum";s:32:"352b4711bca96a9ef711c02ba51a7487";s:4:"name";s:33:"lib/form/sfFormLanguage.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:97:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/form/sfFormLanguage.class.php";}s:66:"lib/validator/doctrine/sfValidatorDoctrineNestedSetLevel.class.php";a:4:{s:6:"md5sum";s:32:"1374000a0198f4a174f80795e60e7c71";s:4:"name";s:66:"lib/validator/doctrine/sfValidatorDoctrineNestedSetLevel.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:130:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/validator/doctrine/sfValidatorDoctrineNestedSetLevel.class.php";}s:44:"lib/validator/sfValidatorBlacklist.class.php";a:4:{s:6:"md5sum";s:32:"47573deff2c7264ae1a31ead15940eed";s:4:"name";s:44:"lib/validator/sfValidatorBlacklist.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:108:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/validator/sfValidatorBlacklist.class.php";}s:44:"lib/validator/sfValidatorReCaptcha.class.php";a:4:{s:6:"md5sum";s:32:"71d741ed1a332e2c3c5e5eba43061123";s:4:"name";s:44:"lib/validator/sfValidatorReCaptcha.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:108:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/validator/sfValidatorReCaptcha.class.php";}s:53:"lib/validator/sfValidatorSchemaTimeInterval.class.php";a:4:{s:6:"md5sum";s:32:"f1af2b598238238258888f18dbc8a257";s:4:"name";s:53:"lib/validator/sfValidatorSchemaTimeInterval.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:117:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/validator/sfValidatorSchemaTimeInterval.class.php";}s:42:"lib/validator/sfValidatorDefault.class.php";a:4:{s:6:"md5sum";s:32:"122f99df59284f63338fd9a1e055e4ff";s:4:"name";s:42:"lib/validator/sfValidatorDefault.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:106:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/validator/sfValidatorDefault.class.php";}s:54:"lib/widget/sfWidgetFormDoctrineChoiceGrouped.class.php";a:4:{s:6:"md5sum";s:32:"b0a625150802410c9a39dee434746908";s:4:"name";s:54:"lib/widget/sfWidgetFormDoctrineChoiceGrouped.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:118:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormDoctrineChoiceGrouped.class.php";}s:60:"lib/widget/sfWidgetFormDoctrineJQueryAutocompleter.class.php";a:4:{s:6:"md5sum";s:32:"8d3f91f1b54429c94b71517bd3840e20";s:4:"name";s:60:"lib/widget/sfWidgetFormDoctrineJQueryAutocompleter.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:124:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormDoctrineJQueryAutocompleter.class.php";}s:52:"lib/widget/sfWidgetFormJQueryAutocompleter.class.php";a:4:{s:6:"md5sum";s:32:"8ca49e7b709485623ebd124950a0a41f";s:4:"name";s:52:"lib/widget/sfWidgetFormJQueryAutocompleter.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:116:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormJQueryAutocompleter.class.php";}s:43:"lib/widget/sfWidgetFormJQueryDate.class.php";a:4:{s:6:"md5sum";s:32:"0a004f96c83c50872775bcc698d5dabb";s:4:"name";s:43:"lib/widget/sfWidgetFormJQueryDate.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:107:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormJQueryDate.class.php";}s:52:"lib/widget/sfWidgetFormPropelChoiceGrouped.class.php";a:4:{s:6:"md5sum";s:32:"8c909201670d42f4d4ddfe46eab45adb";s:4:"name";s:52:"lib/widget/sfWidgetFormPropelChoiceGrouped.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:116:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormPropelChoiceGrouped.class.php";}s:58:"lib/widget/sfWidgetFormPropelJQueryAutocompleter.class.php";a:4:{s:6:"md5sum";s:32:"45adc6b7bfb2296d7bc77a83a441ac88";s:4:"name";s:58:"lib/widget/sfWidgetFormPropelJQueryAutocompleter.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:122:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormPropelJQueryAutocompleter.class.php";}s:42:"lib/widget/sfWidgetFormReCaptcha.class.php";a:4:{s:6:"md5sum";s:32:"9feb211dbd9e4e274b7981f41af339cb";s:4:"name";s:42:"lib/widget/sfWidgetFormReCaptcha.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:106:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormReCaptcha.class.php";}s:49:"lib/widget/sfWidgetFormSelectDoubleList.class.php";a:4:{s:6:"md5sum";s:32:"83860c7eea89ef17f0e4d88268fbb7fc";s:4:"name";s:49:"lib/widget/sfWidgetFormSelectDoubleList.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:113:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormSelectDoubleList.class.php";}s:46:"lib/widget/sfWidgetFormSelectUSState.class.php";a:4:{s:6:"md5sum";s:32:"4b4bedb41a4adb487ecc39cc47be4eab";s:4:"name";s:46:"lib/widget/sfWidgetFormSelectUSState.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:110:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormSelectUSState.class.php";}s:48:"lib/widget/sfWidgetFormTextareaTinyMCE.class.php";a:4:{s:6:"md5sum";s:32:"b4e4b61fba61b26db3ac30bb5933771b";s:4:"name";s:48:"lib/widget/sfWidgetFormTextareaTinyMCE.class.php";s:4:"role";s:4:"data";s:12:"installed_as";s:112:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget/sfWidgetFormTextareaTinyMCE.class.php";}s:32:"test/form/sfFormLanguageTest.php";a:4:{s:6:"md5sum";s:32:"ceecaca65fdf3b4c5e037ab6e696d06a";s:4:"name";s:32:"test/form/sfFormLanguageTest.php";s:4:"role";s:4:"data";s:12:"installed_as";s:96:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/form/sfFormLanguageTest.php";}s:43:"test/validator/sfValidatorReCaptchaTest.php";a:4:{s:6:"md5sum";s:32:"6ea19158046962f6ad76776ed3ad8673";s:4:"name";s:43:"test/validator/sfValidatorReCaptchaTest.php";s:4:"role";s:4:"data";s:12:"installed_as";s:107:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/validator/sfValidatorReCaptchaTest.php";}s:43:"test/validator/sfValidatorBlacklistTest.php";a:4:{s:6:"md5sum";s:32:"0c08b48fbc88fb98aa6062809cba54c8";s:4:"name";s:43:"test/validator/sfValidatorBlacklistTest.php";s:4:"role";s:4:"data";s:12:"installed_as";s:107:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/validator/sfValidatorBlacklistTest.php";}s:52:"test/validator/sfValidatorSchemaTimeIntervalTest.php";a:4:{s:6:"md5sum";s:32:"1f6fd7556f6f3a68b7a31ba859c4175b";s:4:"name";s:52:"test/validator/sfValidatorSchemaTimeIntervalTest.php";s:4:"role";s:4:"data";s:12:"installed_as";s:116:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/validator/sfValidatorSchemaTimeIntervalTest.php";}s:41:"test/validator/sfValidatorDefaultTest.php";a:4:{s:6:"md5sum";s:32:"92b4a5838358108197afe3afe8aee679";s:4:"name";s:41:"test/validator/sfValidatorDefaultTest.php";s:4:"role";s:4:"data";s:12:"installed_as";s:105:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/validator/sfValidatorDefaultTest.php";}s:41:"test/widget/sfWidgetFormReCaptchaTest.php";a:4:{s:6:"md5sum";s:32:"993dc134aefab7538455472af3a3c5a4";s:4:"name";s:41:"test/widget/sfWidgetFormReCaptchaTest.php";s:4:"role";s:4:"data";s:12:"installed_as";s:105:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/widget/sfWidgetFormReCaptchaTest.php";}s:18:"test/bootstrap.php";a:4:{s:6:"md5sum";s:32:"e04220de43865dc7cf4cc7fe5965873b";s:4:"name";s:18:"test/bootstrap.php";s:4:"role";s:4:"data";s:12:"installed_as";s:82:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/bootstrap.php";}s:32:"web/css/jquery.autocompleter.css";a:4:{s:6:"md5sum";s:32:"88def8417a8aefc9fa5d833a4f4d0373";s:4:"name";s:32:"web/css/jquery.autocompleter.css";s:4:"role";s:4:"data";s:12:"installed_as";s:96:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/css/jquery.autocompleter.css";}s:21:"web/js/double_list.js";a:4:{s:6:"md5sum";s:32:"460d935f81fd9de0c01656fd4f294757";s:4:"name";s:21:"web/js/double_list.js";s:4:"role";s:4:"data";s:12:"installed_as";s:85:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/js/double_list.js";}s:30:"web/js/jquery.autocompleter.js";a:4:{s:6:"md5sum";s:32:"b4dc9ff8b1a5569405fe53a1d4dd4f4e";s:4:"name";s:30:"web/js/jquery.autocompleter.js";s:4:"role";s:4:"data";s:12:"installed_as";s:94:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/js/jquery.autocompleter.js";}s:24:"web/images/indicator.gif";a:4:{s:6:"md5sum";s:32:"03ce3dcc84af110e9da8699a841e5200";s:4:"name";s:24:"web/images/indicator.gif";s:4:"role";s:4:"data";s:12:"installed_as";s:88:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/images/indicator.gif";}s:19:"web/images/next.png";a:4:{s:6:"md5sum";s:32:"64113e3f418860c6c27caa83d8494c73";s:4:"name";s:19:"web/images/next.png";s:4:"role";s:4:"data";s:12:"installed_as";s:83:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/images/next.png";}s:23:"web/images/previous.png";a:4:{s:6:"md5sum";s:32:"4485131fafbc7b6bd6018a67671e9040";s:4:"name";s:23:"web/images/previous.png";s:4:"role";s:4:"data";s:12:"installed_as";s:87:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/images/previous.png";}s:6:"README";a:4:{s:6:"md5sum";s:32:"9c80350d278bfbcf24b946d86df8fab6";s:4:"name";s:6:"README";s:4:"role";s:4:"data";s:12:"installed_as";s:70:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/README";}s:7:"LICENSE";a:4:{s:6:"md5sum";s:32:"4db2c5c669396b6c794f2fe214cb33e8";s:4:"name";s:7:"LICENSE";s:4:"role";s:4:"data";s:12:"installed_as";s:71:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/LICENSE";}}s:12:"_lastversion";N;s:7:"dirtree";a:15:{s:67:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/bin";b:1;s:63:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin";b:1;s:72:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/form";b:1;s:67:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib";b:1;s:86:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/validator/doctrine";b:1;s:77:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/validator";b:1;s:74:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/lib/widget";b:1;s:73:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/form";b:1;s:68:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test";b:1;s:78:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/validator";b:1;s:75:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/test/widget";b:1;s:71:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/css";b:1;s:67:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web";b:1;s:70:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/js";b:1;s:74:"/home/gustavo/symfonyreloaded/mobiads/plugins/sfFormExtraPlugin/web/images";b:1;}s:3:"old";a:7:{s:7:"version";s:5:"1.1.3";s:12:"release_date";s:10:"2010-08-25";s:13:"release_state";s:6:"stable";s:15:"release_license";s:11:"MIT license";s:13:"release_notes";s:1:"-";s:12:"release_deps";a:2:{i:0;a:4:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:5:"5.2.4";s:8:"optional";s:2:"no";}i:1;a:6:{s:4:"type";s:3:"pkg";s:7:"channel";s:12:"pear.php.net";s:4:"name";s:4:"PEAR";s:3:"rel";s:2:"ge";s:7:"version";s:5:"1.4.1";s:8:"optional";s:2:"no";}}s:11:"maintainers";a:4:{i:0;a:5:{s:4:"name";s:16:"Fabien Potencier";s:5:"email";s:36:"fabien.potencier@symfony-project.com";s:6:"active";s:3:"yes";s:6:"handle";s:6:"fabpot";s:4:"role";s:4:"lead";}i:1;a:5:{s:4:"name";s:17:"Nicolas Perriault";s:5:"email";s:37:"nicolas.perriault@symfony-project.com";s:6:"active";s:3:"yes";s:6:"handle";s:7:"nicolas";s:4:"role";s:9:"developer";}i:2;a:5:{s:4:"name";s:14:"Kris Wallsmith";s:5:"email";s:34:"kris.wallsmith@symfony-project.com";s:6:"active";s:3:"yes";s:6:"handle";s:14:"Kris.Wallsmith";s:4:"role";s:9:"developer";}i:3;a:5:{s:4:"name";s:10:"Hugo Hamon";s:5:"email";s:21:"hugo.hamon@sensio.com";s:6:"active";s:3:"yes";s:6:"handle";s:10:"hugo.hamon";s:4:"role";s:9:"developer";}}}s:10:"xsdversion";s:3:"2.0";s:13:"_lastmodified";i:1337219155;}
\ No newline at end of file
--- /dev/null
+Copyright (c) 2008 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
--- /dev/null
+sfFormExtraPlugin
+=================
+
+The `sfFormExtraPlugin` packages useful validators, widgets, and forms.
+
+This collection holds validators, widgets, and forms which we don't want to
+include with the main symfony package because they are too specific or have
+external dependencies.
+
+As no third party libraries is bundled in the plugin, you need to install and
+load the required dependencies like JQuery, JQuery UI, or TinyMCE by yourself.
+
+Installation
+------------
+
+ * Install the plugin
+
+ $ symfony plugin:install sfFormExtraPlugin
+
+ * Clear the cache
+
+ $ symfony cache:clear
+
+Documentation
+-------------
+
+All classes have full API and usage documentation. The best way to learn each widget or validator
+is to read the API.
+
+You will also find some articles on the symfony blog about this plugin:
+
+ * [Play with the user language](http://www.symfony-project.org/blog/2008/10/16/play-with-the-user-language)
+ * [Make your Choice!](http://www.symfony-project.org/blog/2008/10/14/new-in-symfony-1-2-make-your-choice)
+ * [Spice up your forms with some nice widgets and validators](http://www.symfony-project.org/blog/2008/10/18/spice-up-your-forms-with-some-nice-widgets-and-validators)
+
+Forms
+-----
+
+ * sfFormLanguage: A form to change the symfony user culture
+
+Validators
+----------
+
+ * sfValidatorDoctrineNestedSetLevel: Checks wether or not the max level of a nested set object (nestedSet behavior) is achieved
+ * sfValidatorReCaptcha: Validates a ReCaptcha (see sfWidgetFormReCaptcha)
+ * sfValidatorBlacklist: Validates that a value is not one of the configured forbidden ones
+ * sfValidatorSchemaTimeInterval: Validates a time interval between two dates provided by a widget schema
+ * sfValidatorDefault: Returns a default value rather than throwing an error
+
+Widgets
+-------
+
+ * sfWidgetFormReCaptcha: Displays a ReCaptcha widget (see sfValidatorReCaptcha)
+ * sfWidgetFormSelectDoubleList: Displays a double list widget
+ * sfWidgetFormJQueryDate: Displays a date using JQuery UI
+ * sfWidgetFormJQueryAutocompleter: Displays an input tag with autocomplete support using JQuery
+ * sfWidgetFormPropelChoiceGrouped: Displays a grouped set of choices tied to a Propel model
+ * sfWidgetFormPropelJQueryAutocompleter: Displays an autocomplete widget tied to a Propel model
+ * sfWidgetFormTextareaTinyMCE: A rich textarea rendered with TinyMCE
+ * sfWidgetFormSelectUSState: A select menu of US states
+
+As no third party libraries is bundled in the plugin, you need to install and load the required
+dependencies like JQuery, JQuery UI, or TinyMCE by yourself.
+
+How to contribute.
+------------------
+
+If you want to contribute a validator, a widget, or a form, follow these steps:
+
+ * Check the prerequisites
+ * The license must be MIT
+ * You must have a unit test suite (100% coverage)
+ * You must have PHPdoc for all classes and methods with a documentation usage
+ * You must follow symfony coding standards
+ * The contribution must not be too specific
+ * You must be sure you will be able to maintain your contribution
+ * Create a ticket and attach a patch
+ * Choose `sfFormExtraPlugin` as the component
+ * Change the qualification to `Ready for core team`
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../test/bootstrap.php';
+
+$h = new lime_harness(new lime_output_color());
+$h->base_dir = realpath(dirname(__FILE__).'/../test');
+$h->register(sfFinder::type('file')->name('*Test.php')->in($h->base_dir));
+$h->run();
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfFormLanguage is a form to change the symfony user culture.
+ *
+ * Usage:
+ *
+ * class mainActions extends sfActions
+ * {
+ * public function executeChangeLanguage($request)
+ * {
+ * $this->form = new sfFormLanguage($this->getUser(), array('languages' => array('en', 'fr')));
+ * if ($this->form->process($request))
+ * {
+ * // culture has changed
+ * return $this->redirect('@homepage');
+ * }
+ *
+ * // the form is not valid (can't happen... but you never know)
+ * return $this->redirect('@homepage');
+ * }
+ * }
+ *
+ * @package symfony
+ * @subpackage form
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfFormLanguage.class.php 30759 2010-08-25 11:48:01Z fabien $
+ */
+class sfFormLanguage extends sfForm
+{
+ protected
+ $user = null;
+
+ /**
+ * Constructor.
+ *
+ * @param sfUser A sfUser instance
+ * @param array An array of options
+ * @param string A CSRF secret (false to disable CSRF protection, null to use the global CSRF secret)
+ *
+ * @see sfForm
+ */
+ public function __construct(sfUser $user, $options = array(), $CSRFSecret = null)
+ {
+ $this->user = $user;
+
+ if (!isset($options['languages']))
+ {
+ throw new RuntimeException(sprintf('%s requires a "languages" option.', get_class($this)));
+ }
+
+ parent::__construct(array('language' => $user->getCulture()), $options, $CSRFSecret);
+ }
+
+ /**
+ * Changes the current user culture.
+ */
+ public function save()
+ {
+ $this->user->setCulture($this->getValue('language'));
+ }
+
+ /**
+ * Processes the current request.
+ *
+ * @param sfRequest A sfRequest instance
+ *
+ * @return Boolean true if the form is valid, false otherwise
+ */
+ public function process(sfRequest $request)
+ {
+ $data = array('language' => $request->getParameter('language'));
+ if ($request->hasParameter(self::$CSRFFieldName))
+ {
+ $data[self::$CSRFFieldName] = $request->getParameter(self::$CSRFFieldName);
+ }
+
+ $this->bind($data);
+
+ if ($isValid = $this->isValid())
+ {
+ $this->save();
+ }
+
+ return $isValid;
+ }
+
+ /**
+ * @see sfForm
+ */
+ public function configure()
+ {
+ $this->setValidators(array(
+ 'language' => new sfValidatorI18nChoiceLanguage(array('languages' => $this->options['languages'])),
+ ));
+
+ $this->setWidgets(array(
+ 'language' => new sfWidgetFormI18nChoiceLanguage(array('culture' => $this->user->getCulture(), 'languages' => $this->options['languages'])),
+ ));
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfValidatorDoctrineNestedSetLevel is a class that validates the max allowed
+ * depth level for Doctrine nested set objects.
+ *
+ * @package symfony
+ * @subpackage validator
+ * @author Hugo Hamon <hugo.hamon@sensio.com>
+ */
+class sfValidatorDoctrineNestedSetLevel extends sfValidatorBase
+{
+ /**
+ * Configures the current validator.
+ *
+ * Available options:
+ *
+ * * max_level: The max depth to test (required integer)
+ * * model: The model class (required)
+ * * alias: The alias of the root component used in the query
+ * * query: A query to use when retrieving objects
+ * * column: The column name for the where clause statement (use primary key by default)
+ * * level_column: The level column name (use level by default)
+ * * connection: The Doctrine connection to use (null by default)
+ *
+ * Available error messages:
+ *
+ * * invalid: The related object has already the max level value
+ * * invalid_record: Unable to find the related object
+ *
+ * @see sfValidatorBase
+ */
+ protected function configure($options = array(), $messages = array())
+ {
+ $this->addRequiredOption('model');
+ $this->addRequiredOption('max_level');
+ $this->addOption('alias', 'a');
+ $this->addOption('query', null);
+ $this->addOption('column', null);
+ $this->addOption('level_column', 'level');
+ $this->addOption('connection', null);
+
+ $this->addMessage('invalid_record', 'Unable to find the related record');
+ }
+
+ /**
+ * @see sfValidatorBase
+ *
+ * @throws sfValidatorError
+ */
+ protected function doClean($value)
+ {
+ $level = $this->getObjectLevelValue($value);
+
+ if ($level >= (int) $this->getOption('max_level'))
+ {
+ throw new sfValidatorError($this, 'invalid', array('value' => $level));
+ }
+
+ return $value;
+ }
+
+ /**
+ * Returns the level column to use for comparison.
+ *
+ * The primary key is used by default.
+ *
+ * @return string The column name
+ */
+ protected function getWhereColumn()
+ {
+ $table = $this->getDoctrineTable();
+
+ if ($this->getOption('column'))
+ {
+ return $table->getColumnName($this->getOption('column'));
+ }
+
+ $identifier = (array) $table->getIdentifier();
+ $columnName = current($identifier);
+
+ return $table->getColumnName($columnName);
+ }
+
+ /**
+ * Returns the level column name
+ *
+ * @return string The column name
+ */
+ protected function getLevelColumn()
+ {
+ $table = $this->getDoctrineTable();
+
+ return $table->getColumnName($this->getOption('level_column'));
+ }
+
+ /**
+ * Returns the Doctrine table object
+ *
+ * @return Doctrine_Table
+ */
+ protected function getDoctrineTable()
+ {
+ return Doctrine::getTable($this->getOption('model'));
+ }
+
+ /**
+ * Returns the object level's value
+ *
+ * @param mixed $value The value of the where column to retrieve the object
+ *
+ * @return int The level value of the found object
+ *
+ * @throws sfValidatorError
+ */
+ protected function getObjectLevelValue($value)
+ {
+ $a = $this->getOption('alias');
+ $q = is_null($this->getOption('query')) ? Doctrine_Query::create()->from($this->getOption('model') . ' ' . $a) : $this->getOption('query');
+ $q->select($a . '.' . $this->getLevelColumn());
+ $q->addWhere($a . '.' . $this->getWhereColumn() . ' = ?', $value);
+
+ $result = $q->fetchOne(array(), Doctrine::HYDRATE_ARRAY);
+
+ if (!$result)
+ {
+ throw new sfValidatorError($this, 'invalid_record', array('value' => $value));
+ }
+
+ return (int) $result[ $this->getLevelColumn() ];
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfValidatorBlacklist validates than the value is not one of the configured
+ * forbidden values. This is a kind of opposite of the sfValidatorChoice
+ * validator.
+ *
+ * @package symfony
+ * @subpackage validator
+ * @author Nicolas Perriault <nicolas.perriault@symfony-project.com>
+ * @version SVN: $Id: sfValidatorChoice.class.php 9048 2008-05-19 09:11:23Z FabianLange $
+ */
+class sfValidatorBlacklist extends sfValidatorBase
+{
+ /**
+ * Configures the current validator.
+ *
+ * Available options:
+ *
+ * * forbidden_values: An array of forbidden values (required)
+ * * case_sensitive: Case sensitive comparison (default true)
+ *
+ * @param array $options An array of options
+ * @param array $messages An array of error messages
+ *
+ * @see sfValidatorBase
+ */
+ protected function configure($options = array(), $messages = array())
+ {
+ $this->addRequiredOption('forbidden_values');
+ $this->addOption('case_sensitive', true);
+ $this->addMessage('forbidden', 'Value %value% is forbidden');
+ }
+
+ /**
+ * @see sfValidatorBase
+ */
+ protected function doClean($value)
+ {
+ $forbiddenValues = $this->getOption('forbidden_values');
+ if ($forbiddenValues instanceof sfCallable)
+ {
+ $forbiddenValues = $forbiddenValues->call();
+ }
+
+ $checkValue = $value;
+
+ if (false === $this->getOption('case_sensitive'))
+ {
+ $checkValue = strtolower($checkValue);
+ $forbiddenValues = array_map('strtolower', $forbiddenValues);
+ }
+
+ if (in_array($checkValue, $forbiddenValues))
+ {
+ throw new sfValidatorError($this, 'forbidden', array('value' => $value));
+ }
+
+ return $value;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Returns a default value instead of throwing an error on validation failure.
+ *
+ * $this->validatorSchema['sort'] = new sfValidatorDefault(array(
+ * 'validator' => new sfValidatorChoice(array('choices' => array('up', 'down'))),
+ * 'default' => 'up',
+ * ));
+ *
+ * If no default option is provided, the supplied validator's empty value will
+ * be returned on error.
+ *
+ * @package sfFormExtraPlugin
+ * @subpackage validator
+ * @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
+ * @version SVN: $Id: sfValidatorDefault.class.php 27625 2010-02-06 22:07:40Z Kris.Wallsmith $
+ */
+class sfValidatorDefault extends sfValidatorBase
+{
+ /**
+ * Configures the current validator.
+ *
+ * Available options:
+ *
+ * * validator: The validator to use
+ * * default: The value to return if the validator fails
+ *
+ * @see sfValidatorBase
+ */
+ protected function configure($options = array(), $messages = array())
+ {
+ $this->addRequiredOption('validator');
+ $this->addOption('default', null);
+ }
+
+ /**
+ * @see sfValidatorBase
+ */
+ protected function isEmpty($value)
+ {
+ return false;
+ }
+
+ /**
+ * @see sfValidatorBase
+ *
+ * @throws InvalidArgumentException If the validator option is not a validator object
+ */
+ protected function doClean($value)
+ {
+ $validator = $this->getOption('validator');
+
+ if (!$validator instanceof sfValidatorBase)
+ {
+ throw new InvalidArgumentException('The "validator" option must be an instance of sfValidatorBase.');
+ }
+
+ try
+ {
+ return $validator->clean($value);
+ }
+ catch (sfValidatorError $error)
+ {
+ return null === $this->getOption('default') ? $validator->getEmptyValue() : $this->getOption('default');
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfValidatorReCaptcha validates a ReCaptcha.
+ *
+ * This validator uses ReCaptcha: http://recaptcha.net/
+ *
+ * The ReCaptcha API documentation can be found at http://recaptcha.net/apidocs/captcha/
+ *
+ * To be able to use this validator, you need an API key: http://recaptcha.net/api/getkey
+ *
+ * To create a captcha validator:
+ *
+ * $captcha = new sfValidatorReCaptcha(array('private_key' => RECAPTCHA_PRIVATE_KEY));
+ *
+ * where RECAPTCHA_PRIVATE_KEY is the ReCaptcha private key.
+ *
+ * @package symfony
+ * @subpackage validator
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfValidatorReCaptcha.class.php 7903 2008-03-15 13:17:41Z fabien $
+ */
+class sfValidatorReCaptcha extends sfValidatorBase
+{
+ /**
+ * Configures the current validator.
+ *
+ * Available options:
+ *
+ * * private_key: The ReCaptcha private key (required)
+ * * remote_addr: The remote address of the user
+ * * server_host: The ReCaptcha server host
+ * * server_port: The ReCaptcha server port
+ * * server_path: The ReCatpcha server path
+ * * server_timeout: The timeout to use when contacting the ReCaptcha server
+ *
+ * Available error codes:
+ *
+ * * captcha
+ * * server_problem
+ *
+ * @see sfValidatorBase
+ */
+ protected function configure($options = array(), $messages = array())
+ {
+ $this->addRequiredOption('private_key');
+
+ $this->addOption('remote_addr');
+ $this->addOption('server_host', 'api-verify.recaptcha.net');
+ $this->addOption('server_port', 80);
+ $this->addOption('server_path', '/verify');
+ $this->addOption('server_timeout', 10);
+
+ $this->addMessage('captcha', 'The captcha is not valid (%error%).');
+ $this->addMessage('server_problem', 'Unable to check the captcha from the server (%error%).');
+ }
+
+ /**
+ * Cleans the input value.
+ *
+ * The input value must be an array with 2 required keys: recaptcha_challenge_field and recaptcha_response_field.
+ *
+ * It always returns null.
+ *
+ * @see sfValidatorBase
+ */
+ protected function doClean($value)
+ {
+ $challenge = isset($value['recaptcha_challenge_field']) ? $value['recaptcha_challenge_field'] : null;
+ $response = isset($value['recaptcha_response_field']) ? $value['recaptcha_response_field'] : null;
+ if (empty($challenge) || empty($response))
+ {
+ throw new sfValidatorError($this, 'captcha', array('error' => 'invalid captcha'));
+ }
+
+ if (true !== ($answer = $this->check(array(
+ 'privatekey' => $this->getOption('private_key'),
+ 'remoteip' => $this->getOption('remote_addr') ? $this->getOption('remote_addr') : $_SERVER['REMOTE_ADDR'],
+ 'challenge' => $challenge,
+ 'response' => $response,
+ ))))
+ {
+ throw new sfValidatorError($this, 'captcha', array('error' => $answer));
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns true if the captcha is valid.
+ *
+ * @param array An array of request parameters
+ *
+ * @return Boolean true if the captcha is valid, false otherwise
+ */
+ protected function check($parameters)
+ {
+ if (false === ($fs = @fsockopen($this->getOption('server_host'), $this->getOption('server_port'), $errno, $errstr, $this->getOption('server_timeout'))))
+ {
+ throw new sfValidatorError($this, 'server_problem', array('error' => $errstr));
+ }
+
+ $query = http_build_query($parameters, null, '&');
+ fwrite($fs, sprintf(
+ "POST %s HTTP/1.0\r\n".
+ "Host: %s\r\n".
+ "Content-Type: application/x-www-form-urlencoded\r\n".
+ "Content-Length: %d\r\n".
+ "User-Agent: reCAPTCHA/PHP/symfony\r\n".
+ "\r\n%s",
+ $this->getOption('server_path'), $this->getOption('server_host'), strlen($query), $query)
+ );
+
+ $response = '';
+ while (!feof($fs))
+ {
+ $response .= fgets($fs, 1160);
+ }
+ fclose($fs);
+
+ $response = explode("\r\n\r\n", $response, 2);
+ $answers = explode("\n", $response[1]);
+
+ return 'true' == trim($answers[0]) ? true : $answers[1];
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * This schema validator validates a time interval between two dates, provided
+ * by two fields in the schema.
+ *
+ * Several options are available:
+ *
+ * - date_start_field: The start date field name
+ * - date_end_field: The end date field name
+ * - min_duration: The minimum duration of interval between the two dates, in seconds (optional)
+ * - max_duration: The maximum duration of interval between the two dates, in seconds (optional)
+ * - disallow_future_dates: Disallows dates in the future (defaults to false)
+ * - disallow_past_dates: Disallows dates in the past (defaults to false)
+ * - throw_global_error: Throws a global error (defaults to false)
+ *
+ * These error codes are available:
+ *
+ * - future_date: A date is in the future
+ * - past_date: A date is in the past
+ * - start_not_prior: The start date is not prior to end date
+ * - too_short: The duration is too short
+ * - too_long: The maximum duration has been exceeded
+ *
+ * Error messages available arguments:
+ *
+ * - date_start: The submitted start date
+ * - date_end: The submitted end date
+ *
+ * @package symfony
+ * @subpackage validator
+ * @author Nicolas Perriault <nicolas.perriault@symfony-project.com>
+ */
+class sfValidatorSchemaTimeInterval extends sfValidatorSchema
+{
+ protected
+ $dateStart = null,
+ $dateEnd = null;
+
+ /**
+ * Public constructor
+ *
+ * @param string $dateStartField The name of the start date field
+ * @param string $dateEndField The name of the end date field
+ * @param array $options Options array
+ * @param array $messages Error messages array
+ */
+ public function __construct($dateStartField, $dateEndField, $options = array(), $messages = array())
+ {
+ // Validator options
+ $this->addOption('date_start_field', $dateStartField);
+ $this->addOption('date_end_field', $dateEndField);
+ $this->addOption('min_duration', null);
+ $this->addOption('max_duration', null);
+ $this->addOption('disallow_future_dates', false);
+ $this->addOption('disallow_past_dates', false);
+ $this->addOption('throw_global_error', false);
+
+ // Validation error messages
+ $this->addMessage('future_date', 'The date cannot be in the future');
+ $this->addMessage('past_date', 'The date cannot be in the past');
+ $this->addMessage('too_short', 'The time interval between the two dates is too short');
+ $this->addMessage('too_long', 'The time interval between the two dates is too long');
+ $this->addMessage('start_not_prior', 'The start date must be prior to the end date');
+
+ // Parent constructor call
+ parent::__construct(null, $options, $messages);
+ }
+
+ /**
+ * Cleans the schema values
+ *
+ * @see sfValidatorSchema
+ *
+ * @param array $values Values to validate
+ * @return array
+ * @throws InvalidArgumentException if $values is not an array
+ * @throws sfValidatorError
+ * @throws sfValidatorErrorSchema
+ */
+ protected function doClean($values)
+ {
+ if (is_null($values))
+ {
+ $values = array();
+ }
+
+ if (!is_array($values))
+ {
+ throw new InvalidArgumentException('You must pass an array parameter to the clean() method');
+ }
+
+ $this->dateStart = isset($values[$this->getOption('date_start_field')]) ? strtotime($values[$this->getOption('date_start_field')]) : null;
+ $this->dateEnd = isset($values[$this->getOption('date_end_field')]) ? strtotime($values[$this->getOption('date_end_field')]) : null;
+
+ $errorCode = null;
+ $errorField = null;
+
+ if ($this->getOption('disallow_future_dates'))
+ {
+ if (!is_null($this->dateStart) && $this->dateStart > time())
+ {
+ $this->throwError('future_date', $this->getOption('date_start_field'));
+ }
+ else if (!is_null($this->dateEnd) && $this->dateEnd > time())
+ {
+ $this->throwError('future_date', $this->getOption('date_end_field'));
+ }
+ }
+
+ if ($this->getOption('disallow_past_dates'))
+ {
+ if (!is_null($this->dateStart) && $this->dateStart < time())
+ {
+ $this->throwError('past_date', $this->getOption('date_start_field'));
+ }
+ else if (!is_null($this->dateEnd) && $this->dateEnd < time())
+ {
+ $this->throwError('past_date', $this->getOption('date_end_field'));
+ }
+ }
+
+ // At this point, if either the start or end date is not set we can return values
+ if (is_null($this->dateStart) or is_null($this->dateEnd))
+ {
+ return $values;
+ }
+
+ // Duration
+ $duration = $this->dateEnd - $this->dateStart;
+
+ if ($this->hasOption('min_duration') && $duration < $this->getOption('min_duration'))
+ {
+ $this->throwError('too_short', $this->getOption('date_end_field'));
+ }
+
+ if ($this->hasOption('max_duration') && $duration > $this->getOption('max_duration'))
+ {
+ $this->throwError('too_long', $this->getOption('date_end_field'));
+ }
+
+ if ($this->dateStart > $this->dateEnd)
+ {
+ $this->throwError('start_not_prior', $this->getOption('date_start_field'));
+ }
+
+ return $values;
+ }
+
+ /**
+ * Throws a validation error
+ *
+ * @param string $code The error code
+ * @param string $field The field related to the error
+ * @throws sfValidatorError
+ * @throws sfValidatorErrorSchema
+ */
+ protected function throwError($code, $field)
+ {
+ $error = new sfValidatorError($this, $code, array(
+ 'date_start' => date('Y-m-d', $this->dateStart),
+ 'date_end' => date('Y-m-d', $this->dateEnd),
+ ));
+
+ if ($this->getOption('throw_global_error'))
+ {
+ throw $error;
+ }
+
+ throw new sfValidatorErrorSchema($this, array($field => $error));
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+/**
+ * A widget of grouped choices.
+ *
+ * @package sfFormExtraPlugin
+ * @subpackage widget
+ * @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormDoctrineChoiceGrouped.class.php 16265 2009-03-12 15:23:41Z Kris.Wallsmith $
+ */
+class sfWidgetFormDoctrineChoiceGrouped extends sfWidgetFormDoctrineChoice
+{
+ /**
+ * Available options:
+ *
+ * * group_by: The name of the relation to use for grouping
+ *
+ * @see sfWidget
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addRequiredOption('group_by');
+
+ parent::configure($options, $attributes);
+ }
+
+ /**
+ * @see sfWidgetFormDoctrineChoice
+ */
+ public function getChoices()
+ {
+ if (is_null($this->getOption('table_method')))
+ {
+ $query = is_null($this->getOption('query')) ? Doctrine::getTable($this->getOption('model'))->createQuery() : $this->getOption('query');
+
+ if ($order = $this->getOption('order_by'))
+ {
+ $query->addOrderBy($order[0].' '.$order[1]);
+ }
+
+ $objects = $query->execute();
+ }
+ else
+ {
+ $tableMethod = $this->getOption('table_method');
+ $results = Doctrine::getTable($this->getOption('model'))->$tableMethod();
+
+ if ($results instanceof Doctrine_Query)
+ {
+ $objects = $results->execute();
+ }
+ else if ($results instanceof Doctrine_Collection)
+ {
+ $objects = $results;
+ }
+ else if ($results instanceof Doctrine_Record)
+ {
+ $objects = new Doctrine_Collection($this->getOption('model'));
+ $objects[] = $results;
+ }
+ else
+ {
+ $objects = array();
+ }
+ }
+
+ $choices = array();
+ if (false !== $this->getOption('add_empty'))
+ {
+ $choices[''] = true === $this->getOption('add_empty') ? '' : $this->getOption('add_empty');
+ }
+
+ $method = $this->getOption('method');
+ $keyMethod = $this->getOption('key_method');
+ $groupBy = $this->getOption('group_by');
+
+ foreach ($objects as $object)
+ {
+ $parent = (string) $object->$groupBy;
+
+ if (!isset($choices[$parent]))
+ {
+ $choices[$parent] = array();
+ }
+
+ $choices[$parent][$object->$keyMethod()] = $object->$method();
+ }
+
+ return $choices;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormDoctrineJQueryAutocompleter represents an autocompleter input widget rendered by JQuery
+ * optimized for foreign key lookup.
+ *
+ * This implementation is based on sfWidgetFormPropelJQueryAutocompleter.
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @author Roland Jungwirth <roland@top-node.com>
+ * @version SVN: $Id: sfWidgetFormPropelJQueryAutocompleter.class.php 12130 2008-10-10 14:51:07Z fabien $
+ */
+class sfWidgetFormDoctrineJQueryAutocompleter extends sfWidgetFormJQueryAutocompleter
+{
+ /**
+ * @see sfWidget
+ */
+ public function __construct($options = array(), $attributes = array())
+ {
+ $options['value_callback'] = array($this, 'toString');
+
+ parent::__construct($options, $attributes);
+ }
+
+ /**
+ * Configures the current widget.
+ *
+ * @param array $options An array of options
+ * @param array $attributes An array of default HTML attributes
+ *
+ * @see sfWidgetFormJQueryAutocompleter
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addRequiredOption('model');
+ $this->addOption('method_for_query', 'findOneById');
+ $this->addOption('method', '__toString');
+
+ parent::configure($options, $attributes);
+ }
+
+ /**
+ * Returns the text representation of a foreign key.
+ *
+ * @param string $value The primary key
+ */
+ protected function toString($value)
+ {
+ $object = null;
+ if ($value != null)
+ {
+ $class = Doctrine::getTable($this->getOption('model'));
+ $method = $this->getOption('method_for_query');
+
+ $object = call_user_func(array($class, $method), $value);
+ }
+
+ $method = $this->getOption('method');
+
+ if (!method_exists($this->getOption('model'), $method))
+ {
+ throw new RuntimeException(sprintf('Class "%s" must implement a "%s" method to be rendered in a "%s" widget', $this->getOption('model'), $method, __CLASS__));
+ }
+
+ return !is_null($object) ? $object->$method() : '';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormJQueryAutocompleter represents an autocompleter input widget rendered by JQuery.
+ *
+ * This widget needs JQuery to work.
+ *
+ * You also need to include the JavaScripts and stylesheets files returned by the getJavaScripts()
+ * and getStylesheets() methods.
+ *
+ * If you use symfony 1.2, it can be done automatically for you.
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormJQueryAutocompleter.class.php 15839 2009-02-27 05:40:57Z fabien $
+ */
+class sfWidgetFormJQueryAutocompleter extends sfWidgetFormInput
+{
+ /**
+ * Configures the current widget.
+ *
+ * Available options:
+ *
+ * * url: The URL to call to get the choices to use (required)
+ * * config: A JavaScript array that configures the JQuery autocompleter widget
+ * * value_callback: A callback that converts the value before it is displayed
+ *
+ * @param array $options An array of options
+ * @param array $attributes An array of default HTML attributes
+ *
+ * @see sfWidgetForm
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addRequiredOption('url');
+ $this->addOption('value_callback');
+ $this->addOption('config', '{ }');
+
+ // this is required as it can be used as a renderer class for sfWidgetFormChoice
+ $this->addOption('choices');
+
+ parent::configure($options, $attributes);
+ }
+
+ /**
+ * @param string $name The element name
+ * @param string $value The date displayed in this widget
+ * @param array $attributes An array of HTML attributes to be merged with the default HTML attributes
+ * @param array $errors An array of errors for the field
+ *
+ * @return string An HTML tag string
+ *
+ * @see sfWidgetForm
+ */
+ public function render($name, $value = null, $attributes = array(), $errors = array())
+ {
+ $visibleValue = $this->getOption('value_callback') ? call_user_func($this->getOption('value_callback'), $value) : $value;
+
+ return $this->renderTag('input', array('type' => 'hidden', 'name' => $name, 'value' => $value)).
+ parent::render('autocomplete_'.$name, $visibleValue, $attributes, $errors).
+ sprintf(<<<EOF
+<script type="text/javascript">
+ jQuery(document).ready(function() {
+ jQuery("#%s")
+ .autocomplete('%s', jQuery.extend({}, {
+ dataType: 'json',
+ parse: function(data) {
+ var parsed = [];
+ for (key in data) {
+ parsed[parsed.length] = { data: [ data[key], key ], value: data[key], result: data[key] };
+ }
+ return parsed;
+ }
+ }, %s))
+ .result(function(event, data) { jQuery("#%s").val(data[1]); });
+ });
+</script>
+EOF
+ ,
+ $this->generateId('autocomplete_'.$name),
+ $this->getOption('url'),
+ $this->getOption('config'),
+ $this->generateId($name)
+ );
+ }
+
+ /**
+ * Gets the stylesheet paths associated with the widget.
+ *
+ * @return array An array of stylesheet paths
+ */
+ public function getStylesheets()
+ {
+ return array('/sfFormExtraPlugin/css/jquery.autocompleter.css' => 'all');
+ }
+
+ /**
+ * Gets the JavaScript paths associated with the widget.
+ *
+ * @return array An array of JavaScript paths
+ */
+ public function getJavascripts()
+ {
+ return array('/sfFormExtraPlugin/js/jquery.autocompleter.js');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormJQueryDate represents a date widget rendered by JQuery UI.
+ *
+ * This widget needs JQuery and JQuery UI to work.
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormJQueryDate.class.php 30755 2010-08-25 11:14:33Z fabien $
+ */
+class sfWidgetFormJQueryDate extends sfWidgetForm
+{
+ /**
+ * Configures the current widget.
+ *
+ * Available options:
+ *
+ * * image: The image path to represent the widget (false by default)
+ * * config: A JavaScript array that configures the JQuery date widget
+ * * culture: The user culture
+ * * date_widget: The date widget instance to use as a "base" class
+ *
+ * @param array $options An array of options
+ * @param array $attributes An array of default HTML attributes
+ *
+ * @see sfWidgetForm
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addOption('image', false);
+ $this->addOption('config', '{}');
+ $this->addOption('culture', '');
+ $this->addOption('date_widget', new sfWidgetFormDate());
+
+ parent::configure($options, $attributes);
+
+ if ('en' == $this->getOption('culture'))
+ {
+ $this->setOption('culture', 'en');
+ }
+ }
+
+ /**
+ * @param string $name The element name
+ * @param string $value The date displayed in this widget
+ * @param array $attributes An array of HTML attributes to be merged with the default HTML attributes
+ * @param array $errors An array of errors for the field
+ *
+ * @return string An HTML tag string
+ *
+ * @see sfWidgetForm
+ */
+ public function render($name, $value = null, $attributes = array(), $errors = array())
+ {
+ $prefix = str_replace('-', '_', $this->generateId($name));
+
+ $image = '';
+ if (false !== $this->getOption('image'))
+ {
+ $image = sprintf(', buttonImage: "%s", buttonImageOnly: true', $this->getOption('image'));
+ }
+
+ if ($this->getOption('date_widget') instanceof sfWidgetFormDateTime)
+ {
+ $years = $this->getOption('date_widget')->getDateWidget()->getOption('years');
+ }
+ else
+ {
+ $years = $this->getOption('date_widget')->getOption('years');
+ }
+
+ return $this->getOption('date_widget')->render($name, $value, $attributes, $errors).
+ $this->renderTag('input', array('type' => 'hidden', 'size' => 10, 'id' => $id = $this->generateId($name).'_jquery_control', 'disabled' => 'disabled')).
+ sprintf(<<<EOF
+<script type="text/javascript">
+ function wfd_%s_read_linked()
+ {
+ jQuery("#%s").val(jQuery("#%s").val() + "-" + jQuery("#%s").val() + "-" + jQuery("#%s").val());
+
+ return {};
+ }
+
+ function wfd_%s_update_linked(date)
+ {
+ jQuery("#%s").val(parseInt(date.substring(0, 4), 10));
+ jQuery("#%s").val(parseInt(date.substring(5, 7), 10));
+ jQuery("#%s").val(parseInt(date.substring(8), 10));
+
+ wfd_%s_check_linked_days();
+ }
+
+ function wfd_%s_check_linked_days()
+ {
+ var daysInMonth = 32 - new Date(jQuery("#%s").val(), jQuery("#%s").val() - 1, 32).getDate();
+
+ jQuery("#%s option").attr("disabled", "");
+ jQuery("#%s option:gt(" + (%s) +")").attr("disabled", "disabled");
+
+ if (jQuery("#%s").val() > daysInMonth)
+ {
+ jQuery("#%s").val(daysInMonth);
+ }
+ }
+
+ jQuery(document).ready(function() {
+ jQuery("#%s").datepicker(jQuery.extend({}, {
+ minDate: new Date(%s, 1 - 1, 1),
+ maxDate: new Date(%s, 12 - 1, 31),
+ beforeShow: wfd_%s_read_linked,
+ onSelect: wfd_%s_update_linked,
+ showOn: "button"
+ %s
+ }, jQuery.datepicker.regional["%s"], %s, {dateFormat: "yy-mm-dd"}));
+ wfd_%s_check_linked_days();
+ });
+
+ jQuery("#%s, #%s, #%s").change(wfd_%s_check_linked_days);
+</script>
+EOF
+ ,
+ $prefix, $id,
+ $this->generateId($name.'[year]'), $this->generateId($name.'[month]'), $this->generateId($name.'[day]'),
+ $prefix,
+ $this->generateId($name.'[year]'), $this->generateId($name.'[month]'), $this->generateId($name.'[day]'),
+ $prefix, $prefix,
+ $this->generateId($name.'[year]'), $this->generateId($name.'[month]'),
+ $this->generateId($name.'[day]'), $this->generateId($name.'[day]'),
+ ($this->getOption('date_widget')->getOption('can_be_empty') ? 'daysInMonth' : 'daysInMonth - 1'),
+ $this->generateId($name.'[day]'), $this->generateId($name.'[day]'),
+ $id,
+ min($years), max($years),
+ $prefix, $prefix, $image, $this->getOption('culture'), $this->getOption('config'),
+ $prefix,
+ $this->generateId($name.'[day]'), $this->generateId($name.'[month]'), $this->generateId($name.'[year]'),
+ $prefix
+ );
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * A widget of grouped choices.
+ *
+ * @package sfFormExtraPlugin
+ * @subpackage widget
+ * @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormPropelChoiceGrouped.class.php 16067 2009-03-06 16:57:46Z Kris.Wallsmith $
+ */
+class sfWidgetFormPropelChoiceGrouped extends sfWidgetFormPropelChoice
+{
+ /**
+ * Available options:
+ *
+ * * group_by_method: A method on the current model that will return the
+ * object the widget is grouped by (i.e. getAuthor)
+ *
+ * @see sfWidget
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addRequiredOption('group_by_method');
+
+ parent::configure($options, $attributes);
+ }
+
+ /**
+ * @see sfWidgetFormPropelChoice
+ */
+ public function getChoices()
+ {
+ $choices = array();
+ if (!$this->getOption('expanded') && false !== $this->getOption('add_empty'))
+ {
+ $choices[''] = true === $this->getOption('add_empty') ? '' : $this->getOption('add_empty');
+ }
+
+ $methodKey = $this->getOption('key_method');
+ if (!method_exists($this->getOption('model'), $methodKey))
+ {
+ throw new RuntimeException(sprintf('Class "%s" must implement a "%s" method to be rendered in a "%s" widget', $this->getOption('model'), $methodKey, __CLASS__));
+ }
+
+ $methodValue = $this->getOption('method');
+ if (!method_exists($this->getOption('model'), $methodValue))
+ {
+ throw new RuntimeException(sprintf('Class "%s" must implement a "%s" method to be rendered in a "%s" widget', $this->getOption('model'), $methodValue, __CLASS__));
+ }
+
+ $methodParent = $this->getOption('group_by_method');
+ if (!method_exists($this->getOption('model'), $methodParent))
+ {
+ throw new RuntimeException(sprintf('Class "%s" must implement a "%s" method to be rendered in a "%s" widget', $this->getOption('model'), $methodParent, __CLASS__));
+ }
+
+ $class = constant($this->getOption('model').'::PEER');
+
+ $criteria = is_null($this->getOption('criteria')) ? new Criteria() : clone $this->getOption('criteria');
+ if ($order = $this->getOption('order_by'))
+ {
+ $method = sprintf('add%sOrderByColumn', 0 === strpos(strtoupper($order[1]), 'ASC') ? 'Ascending' : 'Descending');
+ $criteria->$method(call_user_func(array($class, 'translateFieldName'), $order[0], BasePeer::TYPE_PHPNAME, BasePeer::TYPE_COLNAME));
+ }
+
+ if ('doSelect' == $peerMethod = $this->getOption('peer_method'))
+ {
+ // apply join method
+ $peerMethod = 'doSelectJoin'.substr($this->getOption('group_by_method'), 3);
+ }
+ $objects = call_user_func(array($class, $peerMethod), $criteria, $this->getOption('connection'));
+
+ foreach ($objects as $object)
+ {
+ $parent = $object->$methodParent($this->getOption('connection'));
+ if (!method_exists($parent, '__toString'))
+ {
+ throw new RuntimeException(sprintf('Class "%s" must implement a "__toString" method to be rendered in a "%s" widget', get_class($parent), __CLASS__));
+ }
+
+ $parentValue = (string) $parent;
+ if (!isset($choices[$parentValue]))
+ {
+ $choices[$parentValue] = array();
+ }
+
+ $choices[$parentValue][$object->$methodKey()] = $object->$methodValue();
+ }
+
+ return $choices;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormPropelJQueryAutocompleter represents an autocompleter input widget rendered by JQuery
+ * optimized for foreign key lookup.
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormPropelJQueryAutocompleter.class.php 12130 2008-10-10 14:51:07Z fabien $
+ */
+class sfWidgetFormPropelJQueryAutocompleter extends sfWidgetFormJQueryAutocompleter
+{
+ /**
+ * @see sfWidget
+ */
+ public function __construct($options = array(), $attributes = array())
+ {
+ $options['value_callback'] = array($this, 'toString');
+
+ parent::__construct($options, $attributes);
+ }
+
+ /**
+ * Configures the current widget.
+ *
+ * @param array $options An array of options
+ * @param array $attributes An array of default HTML attributes
+ *
+ * @see sfWidgetFormJQueryAutocompleter
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addRequiredOption('model');
+ $this->addOption('method', '__toString');
+
+ parent::configure($options, $attributes);
+ }
+
+ /**
+ * Returns the text representation of a foreign key.
+ *
+ * @param string $value The primary key
+ */
+ protected function toString($value)
+ {
+ $class = constant($this->getOption('model').'::PEER');
+
+ $criteria = new Criteria();
+ $object = call_user_func(array($class, 'retrieveByPK'), $value);
+
+ $method = $this->getOption('method');
+
+ if (!method_exists($this->getOption('model'), $method))
+ {
+ throw new RuntimeException(sprintf('Class "%s" must implement a "%s" method to be rendered in a "%s" widget', $this->getOption('model'), $method, __CLASS__));
+ }
+
+ return !is_null($object) ? $object->$method() : '';
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormReCaptcha renders a ReCaptcha widget.
+ *
+ * This widget uses ReCaptcha: http://recaptcha.net/
+ *
+ * The ReCaptcha API documentation can be found at http://recaptcha.net/apidocs/captcha/
+ *
+ * To be able to use this widget, you need an API key: http://recaptcha.net/api/getkey
+ *
+ * As it's not possible to change the name of ReCaptcha fields, you will have to add them manually
+ * when binding a form from an HTTP request.
+ *
+ * Here's a typical usage when embedding a captcha in a form with a contact[%s] name format:
+ *
+ * $captcha = array(
+ * 'recaptcha_challenge_field' => $request->getParameter('recaptcha_challenge_field'),
+ * 'recaptcha_response_field' => $request->getParameter('recaptcha_response_field'),
+ * );
+ * $this->form->bind(array_merge($request->getParameter('contact'), array('captcha' => $captcha)));
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormReCaptcha.class.php 30758 2010-08-25 11:39:13Z fabien $
+ */
+class sfWidgetFormReCaptcha extends sfWidgetForm
+{
+ /**
+ * Configures the current widget.
+ *
+ * Available options:
+ *
+ * * public_key: The ReCaptcha public key
+ * * use_ssl: Whether to use SSL or not (false by default)
+ * * server_url: The URL for the HTTP API
+ * * server_url_ssl: The URL for the HTTPS API (when use_ssl is true)
+ * * theme: The ReCaptcha theme
+ * * culture: The ReCaptcha language
+ *
+ * @see sfWidgetForm
+ */
+ public function configure($options = array(), $messages = array())
+ {
+ $this->addRequiredOption('public_key');
+
+ $this->addOption('use_ssl', false);
+ $this->addOption('server_url', 'http://api.recaptcha.net');
+ $this->addOption('server_url_ssl', 'https://api-secure.recaptcha.net');
+ $this->addOption('theme', 'clean');
+ $this->addOption('culture', 'en');
+ }
+
+ /**
+ * @see sfWidgetForm
+ */
+ public function render($name, $value = null, $attributes = array(), $errors = array())
+ {
+ $server = $this->getServerUrl();
+ $key = $this->getOption('public_key');
+ $theme = $this->getOption('theme');
+ $culture = $this->getOption('culture');
+
+ return sprintf('
+ <script type="text/javascript">
+ var RecaptchaOptions = {
+ theme : \'%s\',
+ lang : \'%s\'
+ };
+ </script>
+ <script type="text/javascript" src="%s/challenge?k=%s"></script>
+ <noscript>
+ <iframe src="%s/noscript?k=%s" height="300" width="500" frameborder="0"></iframe><br />
+ <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
+ <input type="hidden" name="recaptcha_response_field" value="manual_challenge" />
+ </noscript>
+ ', $theme, $culture, $server, $key, $server, $key);
+ }
+
+ protected function getServerUrl()
+ {
+ return $this->getOption('use_ssl') ? $this->getOption('server_url_ssl') : $this->getOption('server_url');
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormSelectDoubleList represents a multiple select displayed as a double list.
+ *
+ * This widget needs some JavaScript to work. So, you need to include the JavaScripts
+ * files returned by the getJavaScripts() method.
+ *
+ * If you use symfony 1.2, it can be done automatically for you.
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormSelectDoubleList.class.php 30760 2010-08-25 11:50:26Z fabien $
+ */
+class sfWidgetFormSelectDoubleList extends sfWidgetForm
+{
+ /**
+ * Constructor.
+ *
+ * Available options:
+ *
+ * * choices: An array of possible choices (required)
+ * * class: The main class of the widget
+ * * class_select: The class for the two select tags
+ * * label_unassociated: The label for unassociated
+ * * label_associated: The label for associated
+ * * unassociate: The HTML for the unassociate link
+ * * associate: The HTML for the associate link
+ * * associated_first: Whether the associated list if first (true by default)
+ * * template: The HTML template to use to render this widget
+ * The available placeholders are:
+ * * label_associated
+ * * label_unassociated
+ * * associate
+ * * unassociate
+ * * associated
+ * * unassociated
+ * * class
+ *
+ * @param array $options An array of options
+ * @param array $attributes An array of default HTML attributes
+ *
+ * @see sfWidgetForm
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addRequiredOption('choices');
+
+ $this->addOption('class', 'double_list');
+ $this->addOption('class_select', 'double_list_select');
+ $this->addOption('associated_first', true);
+ $this->addOption('label_unassociated', 'Unassociated');
+ $this->addOption('label_associated', 'Associated');
+ $associated_first = isset($options['associated_first']) ? $options['associated_first'] : true;
+
+ if ($associated_first)
+ {
+ $associate_image = 'previous.png';
+ $unassociate_image = 'next.png';
+ $float = 'left';
+ }
+ else
+ {
+ $associate_image = 'next.png';
+ $unassociate_image = 'previous.png';
+ $float = 'right';
+ }
+
+ $this->addOption('unassociate', '<img src="/sfFormExtraPlugin/images/'.$unassociate_image.'" alt="unassociate" />');
+ $this->addOption('associate', '<img src="/sfFormExtraPlugin/images/'.$associate_image.'" alt="associate" />');
+ $this->addOption('template', <<<EOF
+<div class="%class%">
+ <div style="float: left">
+ <div style="float: $float">
+ <div class="double_list_label">%label_associated%</div>
+ %associated%
+ </div>
+ <div style="float: $float; margin-top: 2em">
+ %associate%
+ <br />
+ %unassociate%
+ </div>
+ <div style="float: $float">
+ <div class="double_list_label">%label_unassociated%</div>
+ %unassociated%
+ </div>
+ </div>
+ <br style="clear: both" />
+ <script type="text/javascript">
+ sfDoubleList.init(document.getElementById('%id%'), '%class_select%');
+ </script>
+</div>
+EOF
+);
+ }
+
+ /**
+ * Renders the widget.
+ *
+ * @param string $name The element name
+ * @param string $value The value selected in this widget
+ * @param array $attributes An array of HTML attributes to be merged with the default HTML attributes
+ * @param array $errors An array of errors for the field
+ *
+ * @return string An HTML tag string
+ *
+ * @see sfWidgetForm
+ */
+ public function render($name, $value = null, $attributes = array(), $errors = array())
+ {
+ if (is_null($value))
+ {
+ $value = array();
+ }
+
+ $choices = $this->getOption('choices');
+ if ($choices instanceof sfCallable)
+ {
+ $choices = $choices->call();
+ }
+
+ $associated = array();
+ $unassociated = array();
+ foreach ($choices as $key => $option)
+ {
+ if (in_array(strval($key), $value))
+ {
+ $associated[$key] = $option;
+ }
+ else
+ {
+ $unassociated[$key] = $option;
+ }
+ }
+
+ $size = isset($attributes['size']) ? $attributes['size'] : (isset($this->attributes['size']) ? $this->attributes['size'] : 10);
+
+ $associatedWidget = new sfWidgetFormSelect(array('multiple' => true, 'choices' => $associated), array('size' => $size, 'class' => $this->getOption('class_select').'-selected'));
+ $unassociatedWidget = new sfWidgetFormSelect(array('multiple' => true, 'choices' => $unassociated), array('size' => $size, 'class' => $this->getOption('class_select')));
+
+ return strtr($this->getOption('template'), array(
+ '%class%' => $this->getOption('class'),
+ '%class_select%' => $this->getOption('class_select'),
+ '%id%' => $this->generateId($name),
+ '%label_associated%' => $this->getOption('label_associated'),
+ '%label_unassociated%' => $this->getOption('label_unassociated'),
+ '%associate%' => sprintf('<a href="#" onclick="%s">%s</a>', 'sfDoubleList.move(\'unassociated_'.$this->generateId($name).'\', \''.$this->generateId($name).'\'); return false;', $this->getOption('associate')),
+ '%unassociate%' => sprintf('<a href="#" onclick="%s">%s</a>', 'sfDoubleList.move(\''.$this->generateId($name).'\', \'unassociated_'.$this->generateId($name).'\'); return false;', $this->getOption('unassociate')),
+ '%associated%' => $associatedWidget->render($name),
+ '%unassociated%' => $unassociatedWidget->render('unassociated_'.$name),
+ ));
+ }
+
+ /**
+ * Gets the JavaScript paths associated with the widget.
+ *
+ * @return array An array of JavaScript paths
+ */
+ public function getJavascripts()
+ {
+ return array('/sfFormExtraPlugin/js/double_list.js');
+ }
+
+ public function __clone()
+ {
+ if ($this->getOption('choices') instanceof sfCallable)
+ {
+ $callable = $this->getOption('choices')->getCallable();
+ if (is_array($callable))
+ {
+ $callable[0] = $this;
+ $this->setOption('choices', new sfCallable($callable));
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Select menu of US states.
+ *
+ * Here's an example usage:
+ *
+ * $this->setWidgets(array(
+ * 'state' => new sfWidgetFormSelectUSState(array('add_empty' => 'Select a state...')),
+ * ));
+ *
+ * $this->setValidators(array(
+ * 'state' => sfValidatorChoice(array('choices' => sfWidgetFormSelectUSState::getStateAbbreviations())),
+ * ));
+ *
+ * @package sfFormExtraPlugin
+ * @subpackage widget
+ * @author Kris Wallsmith <kris.wallsmith@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormSelectUSState.class.php 15371 2009-02-09 19:19:20Z Kris.Wallsmith $
+ */
+class sfWidgetFormSelectUSState extends sfWidgetFormSelect
+{
+ /**
+ * @see sfWidget
+ */
+ public function __construct($options = array(), $attributes = array())
+ {
+ $options['choices'] = new sfCallable(array($this, 'getChoices'));
+
+ parent::__construct($options, $attributes);
+ }
+
+ /**
+ * @see sfWidget
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addOption('add_empty', false);
+
+ parent::configure($options, $attributes);
+ }
+
+ /**
+ * Returns choices for the current widget.
+ *
+ * @return array
+ */
+ public function getChoices()
+ {
+ $choices = array();
+ if (false !== $this->getOption('add_empty'))
+ {
+ $choices[''] = true === $this->getOption('add_empty') ? '' : $this->getOption('add_empty');
+ }
+
+ $choices = array_merge($choices, self::getStates());
+
+ return $choices;
+ }
+
+ /**
+ * Returns an associative array of US states.
+ *
+ * @return array
+ */
+ static public function getStates()
+ {
+ return self::$states;
+ }
+
+ /**
+ * Sets the array of states.
+ *
+ * @param array $states
+ */
+ static public function setStates(array $states)
+ {
+ self::$states = $states;
+ }
+
+ /**
+ * Returns an array of state abbreviations.
+ *
+ * @return array
+ */
+ static public function getStateAbbreviations()
+ {
+ return array_keys(self::$states);
+ }
+
+ static protected $states = array(
+ 'AL' => 'Alabama',
+ 'AK' => 'Alaska',
+ 'AZ' => 'Arizona',
+ 'AR' => 'Arkansas',
+ 'CA' => 'California',
+ 'CO' => 'Colorado',
+ 'CT' => 'Connecticut',
+ 'DE' => 'Delaware',
+ 'DC' => 'District Of Columbia',
+ 'FL' => 'Florida',
+ 'GA' => 'Georgia',
+ 'HI' => 'Hawaii',
+ 'ID' => 'Idaho',
+ 'IL' => 'Illinois',
+ 'IN' => 'Indiana',
+ 'IA' => 'Iowa',
+ 'KS' => 'Kansas',
+ 'KY' => 'Kentucky',
+ 'LA' => 'Louisiana',
+ 'ME' => 'Maine',
+ 'MD' => 'Maryland',
+ 'MA' => 'Massachusetts',
+ 'MI' => 'Michigan',
+ 'MN' => 'Minnesota',
+ 'MS' => 'Mississippi',
+ 'MO' => 'Missouri',
+ 'MT' => 'Montana',
+ 'NE' => 'Nebraska',
+ 'NV' => 'Nevada',
+ 'NH' => 'New Hampshire',
+ 'NJ' => 'New Jersey',
+ 'NM' => 'New Mexico',
+ 'NY' => 'New York',
+ 'NC' => 'North Carolina',
+ 'ND' => 'North Dakota',
+ 'OH' => 'Ohio',
+ 'OK' => 'Oklahoma',
+ 'OR' => 'Oregon',
+ 'PA' => 'Pennsylvania',
+ 'RI' => 'Rhode Island',
+ 'SC' => 'South Carolina',
+ 'SD' => 'South Dakota',
+ 'TN' => 'Tennessee',
+ 'TX' => 'Texas',
+ 'UT' => 'Utah',
+ 'VT' => 'Vermont',
+ 'VA' => 'Virginia',
+ 'WA' => 'Washington',
+ 'WV' => 'West Virginia',
+ 'WI' => 'Wisconsin',
+ 'WY' => 'Wyoming',
+ );
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormTextareaTinyMCE represents a Tiny MCE widget.
+ *
+ * You must include the Tiny MCE JavaScript file by yourself.
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Fabien Potencier <fabien.potencier@symfony-project.com>
+ * @version SVN: $Id: sfWidgetFormTextareaTinyMCE.class.php 17192 2009-04-10 07:58:29Z fabien $
+ */
+class sfWidgetFormTextareaTinyMCE extends sfWidgetFormTextarea
+{
+ /**
+ * Constructor.
+ *
+ * Available options:
+ *
+ * * theme: The Tiny MCE theme
+ * * width: Width
+ * * height: Height
+ * * config: The javascript configuration
+ *
+ * @param array $options An array of options
+ * @param array $attributes An array of default HTML attributes
+ *
+ * @see sfWidgetForm
+ */
+ protected function configure($options = array(), $attributes = array())
+ {
+ $this->addOption('theme', 'advanced');
+ $this->addOption('width');
+ $this->addOption('height');
+ $this->addOption('config', '');
+ }
+
+ /**
+ * @param string $name The element name
+ * @param string $value The value selected in this widget
+ * @param array $attributes An array of HTML attributes to be merged with the default HTML attributes
+ * @param array $errors An array of errors for the field
+ *
+ * @return string An HTML tag string
+ *
+ * @see sfWidgetForm
+ */
+ public function render($name, $value = null, $attributes = array(), $errors = array())
+ {
+ $textarea = parent::render($name, $value, $attributes, $errors);
+
+ $js = sprintf(<<<EOF
+<script type="text/javascript">
+ tinyMCE.init({
+ mode: "exact",
+ elements: "%s",
+ theme: "%s",
+ %s
+ %s
+ theme_advanced_toolbar_location: "top",
+ theme_advanced_toolbar_align: "left",
+ theme_advanced_statusbar_location: "bottom",
+ theme_advanced_resizing: true
+ %s
+ });
+</script>
+EOF
+ ,
+ $this->generateId($name),
+ $this->getOption('theme'),
+ $this->getOption('width') ? sprintf('width: "%spx",', $this->getOption('width')) : '',
+ $this->getOption('height') ? sprintf('height: "%spx",', $this->getOption('height')) : '',
+ $this->getOption('config') ? ",\n".$this->getOption('config') : ''
+ );
+
+ return $textarea.$js;
+ }
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+if (!isset($_SERVER['SYMFONY']))
+{
+ die("You must set the \"SYMFONY\" environment variable to the symfony lib dir (export SYMFONY=/path/to/symfony/lib/).\n");
+}
+
+require_once $_SERVER['SYMFONY'].'/vendor/lime/lime.php';
+require_once $_SERVER['SYMFONY'].'/autoload/sfCoreAutoload.class.php';
+sfCoreAutoload::register();
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../bootstrap.php';
+require_once dirname(__FILE__).'/../../lib/form/sfFormLanguage.class.php';
+
+$t = new lime_test(9, new lime_output_color());
+
+// initialize objects
+$dispatcher = new sfEventDispatcher();
+
+$sessionPath = sys_get_temp_dir().'/sessions_'.rand(11111, 99999);
+$storage = new sfSessionTestStorage(array('session_path' => $sessionPath));
+$user = new sfUser($dispatcher, $storage);
+$user->setCulture('en');
+
+$request = new sfWebRequest($dispatcher);
+
+// __construct()
+$t->diag('__construct()');
+try
+{
+ new sfFormLanguage($user);
+ $t->fail('__construct() throws a RuntimeException if you don\'t pass a "languages" option');
+}
+catch (RuntimeException $e)
+{
+ $t->pass('__construct() throws a RuntimeException if you don\'t pass a "languages" option');
+}
+$form = new sfFormLanguage($user, array('languages' => array('en', 'fr')));
+$t->is($form->getDefault('language'), 'en', '__construct() sets the default language value to the user language');
+$w = $form->getWidgetSchema();
+$t->is($w['language']->getOption('languages'), array('en', 'fr'), '__construct() uses the "languages" option for the select form widget');
+$v = $form->getValidatorSchema();
+$t->is($v['language']->getOption('languages'), array('en', 'fr'), '__construct() uses the "languages" option for the validator');
+
+// ->process()
+$t->diag('->process()');
+
+// with CSRF disabled
+$t->diag('with CSRF disabled');
+sfForm::disableCSRFProtection();
+
+$form = new sfFormLanguage($user, array('languages' => array('en', 'fr')));
+$request->setParameter('language', 'fr');
+$t->is($form->process($request), true, '->process() returns true if the form is valid');
+$t->is($user->getCulture(), 'fr', '->process() changes the user culture');
+
+$request->setParameter('language', 'es');
+$t->is($form->process($request), false, '->process() returns true if the form is not valid');
+$t->is($form['language']->getError()->getCode(), 'invalid', '->process() throws an error if the language is not in the languages option');
+
+sfToolkit::clearDirectory($sessionPath);
+
+// with CSRF enabled
+$t->diag('with CSRF enabled');
+sfForm::enableCSRFProtection('secret');
+
+$form = new sfFormLanguage($user, array('languages' => array('en', 'fr')));
+$request->setParameter('language', 'fr');
+$request->setParameter('_csrf_token', $form->getCSRFToken('secret'));
+$t->is($form->process($request), true, '->process() returns true if the form is valid');
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../bootstrap.php';
+require_once dirname(__FILE__).'/../../lib/validator/sfValidatorBlacklist.class.php';
+
+$t = new lime_test(6, new lime_output_color());
+
+// __construct()
+$t->diag('__construct()');
+try
+{
+ new sfValidatorBlacklist();
+ $t->fail('__construct() expects a "forbidden_values" option');
+}
+catch (RuntimeException $e)
+{
+ $t->pass('__construct() expects a "forbidden_values" option');
+}
+
+$v = new sfValidatorBlacklist(array('forbidden_values' => array('foo', 'bar', 'baz')));
+
+// ->clean()
+$t->diag('->clean()');
+try
+{
+ $v->clean('foo'); // "foo" is a forbidden value
+ $t->fail('->clean() throws a sfValidatorError when the submitted value is invalid');
+ $t->skip();
+}
+catch (sfValidatorError $e)
+{
+ $t->pass('->clean() throws a sfValidatorError when the submitted value is invalid');
+ $t->is($e->getCode(), 'forbidden', '->clean() throws an "forbidden" error');
+}
+
+try
+{
+ $v->clean('gonzagues'); // "gonzagues" is not a forbidden value
+ $t->pass('->clean() does not throw a sfValidatorError when the submitted value is valid');
+}
+catch (sfValidatorError $e)
+{
+ $t->fail('->clean() does not throw a sfValidatorError when the submitted value is valid');
+}
+
+$v = new sfValidatorBlacklist(array(
+ 'forbidden_values' => array('Foo', 'bAr', 'baZ'),
+ 'case_sensitive' => false,
+));
+
+try
+{
+ $v->clean('baz'); // "baz" is a forbidden value in a case sensitive context
+ $t->fail('->clean() throws a sfValidatorError when the submitted value is invalid in a case-insensitive context');
+}
+catch (sfValidatorError $e)
+{
+ $t->pass('->clean() throws a sfValidatorError when the submitted value is invalid in a case-insensitive context');
+}
+
+$t->is($v->clean('FOOBAR'), 'FOOBAR', '->clean() does not change the value\'s case');
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../bootstrap.php';
+require_once dirname(__FILE__).'/../../lib/validator/sfValidatorDefault.class.php';
+
+$t = new lime_test(4);
+
+// ->clean()
+$t->diag('->clean()');
+
+$validator = new sfValidatorDefault(array(
+ 'validator' => new sfValidatorString(),
+ 'default' => '==DEFAULT==',
+));
+$t->is($validator->clean('foo'), 'foo', '->clean() returns cleaned values');
+$t->is($validator->clean(null), '==DEFAULT==', '->clean() returns the default on validation failure');
+
+$validator = new sfValidatorDefault(array(
+ 'validator' => new sfValidatorString(array('empty_value' => '==EMPTY==')),
+));
+$t->is($validator->clean(null), '==EMPTY==', '->clean() returns the validator empty value if no default value is set');
+
+$validator = new sfValidatorDefault(array(
+ 'validator' => new sfValidatorString(array('empty_value' => '==EMPTY==')),
+ 'default' => '==DEFAULT==',
+));
+$t->is($validator->clean(null), '==DEFAULT==', '->clean() returns the default if both default and the embedded empty value are set');
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../bootstrap.php';
+require_once dirname(__FILE__).'/../../lib/validator/sfValidatorReCaptcha.class.php';
+
+$t = new lime_test(3, new lime_output_color());
+
+$REMOTE_ADDR = 'symfony.example.com';
+$PUBLIC_KEY = '6Lf2DQEAAAAAALB9pGaVdcjMiv4CAuOVkfCSVvGh';
+$PRIVATE_KEY = '6Lf2DQEAAAAAALnEL0iEogIxZNYMlG7pmNhwEXjk';
+
+// __construct()
+$t->diag('__construct()');
+try
+{
+ new sfValidatorReCaptcha();
+}
+catch (RuntimeException $e)
+{
+ $t->pass('__construct() expects a "private_key" option');
+}
+
+// ->clean()
+$t->diag('->clean()');
+$v = new sfValidatorReCaptcha(array('private_key' => $PRIVATE_KEY, 'remote_addr' => $REMOTE_ADDR));
+try
+{
+ $v->clean(array(
+ 'recaptcha_challenge_field' => null,
+ 'recaptcha_response_field' => null,
+ 'captcha' => null,
+ ));
+ $t->fail('->clean() throws a sfValidatorError when the captcha is invalid');
+ $t->skip();
+}
+catch (sfValidatorError $e)
+{
+ $t->pass('->clean() throws a sfValidatorError when the captcha is invalid');
+ $t->is($e->getCode(), 'captcha', '->clean() throws a captcha code');
+}
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../bootstrap.php';
+require_once dirname(__FILE__).'/../../lib/validator/sfValidatorSchemaTimeInterval.class.php';
+
+$t = new lime_test(14, new lime_output_color());
+
+// __construct()
+$t->diag('__construct()');
+$v = new sfValidatorSchemaTimeInterval('start', 'end');
+$t->is($v->getOption('date_start_field'), 'start', '__construct() assigns date start field option');
+$t->is($v->getOption('date_end_field'), 'end', '__construct() assigns date end field option');
+
+// clean()
+$t->diag('clean()');
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_future_dates' => false, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => null, 'end' => null));
+ $t->pass('clean() validates if no dates are set');
+}
+catch (sfValidatorErrorSchema $e)
+{
+ $t->fail('clean() validates if no dates are set');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_future_dates' => false, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => '2008-01-01', 'end' => '2007-01-01'));
+ $t->fail('clean() throws a sfValidatorError');
+ $t->fail('clean() detects start date greater than end date');
+}
+catch (sfValidatorErrorSchema $e)
+{
+ $t->isa_ok($e, 'sfValidatorErrorSchema', 'clean() throws a sfValidatorErrorSchema');
+ $t->is($e->getCode(), 'start [start_not_prior]', 'clean() detects start date greater than end date');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_future_dates' => true, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => date('Y-m-d', time() + (365*24*3600)), 'end' => date('Y-m-d', time() + (366*24*3600))));
+ $t->fail('clean() detects future date');
+}
+catch (sfValidatorErrorSchema $e)
+{
+ $t->is($e->getCode(), 'start [future_date]', 'clean() detects future date');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_past_dates' => true, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => date('Y-m-d', time() - (365*24*3600)), 'end' => date('Y-m-d', time() - (366*24*3600))));
+ $t->fail('clean() detects past date');
+}
+catch (sfValidatorErrorSchema $e)
+{
+ $t->is($e->getCode(), 'start [past_date]', 'clean() detects past date');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_future_dates' => false, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => date('Y-m-d', time() + (365*24*3600)), 'end' => date('Y-m-d', time() + (366*24*3600))));
+ $t->pass('clean() validates future dates if option allows it');
+}
+catch (sfValidatorErrorSchema $e)
+{
+ $t->fail('clean() validates future dates if option allows it');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_future_dates' => false, 'throw_global_error' => true));
+try
+{
+ $v->clean(array('start' => '2008-01-01', 'end' => '2007-01-01'));
+ $t->fail('clean() throws a gobal error if option is set');
+}
+catch (sfValidatorError $e)
+{
+ $t->isa_ok($e, 'sfValidatorError', 'clean() throws a gobal error if option is set');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_future_dates' => false, 'throw_global_error' => true));
+try
+{
+ $v->clean(array('start' => '2008-01-01', 'end' => null));
+ $t->pass('clean() validates if end date is not set');
+}
+catch (sfValidatorError $e)
+{
+ $t->fail('clean() validates if end date is not set');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('min_duration' => 86400 * 10, 'disallow_future_dates' => false, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => '2008-01-01', 'end' => '2008-01-09'));
+ $t->fail('clean() detects a too short duration');
+}
+catch (sfValidatorError $e)
+{
+ $t->is($e->getCode(), 'end [too_short]', 'clean() detects a too short duration');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('max_duration' => 86400 * 10, 'disallow_future_dates' => false, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => '2008-01-01', 'end' => '2008-01-12'));
+ $t->fail('clean() detects an exceeded duration');
+}
+catch (sfValidatorError $e)
+{
+ $t->is($e->getCode(), 'end [too_long]', 'clean() detects an exceeded duration');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_future_dates' => true, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => null, 'end' => date('Y-m-d', time() + (10*86400))));
+ $t->fail('clean() detects an unique future date');
+}
+catch (sfValidatorError $e)
+{
+ $t->pass('clean() detects an unique future date');
+}
+
+$v = new sfValidatorSchemaTimeInterval('start', 'end', array('disallow_past_dates' => true, 'throw_global_error' => false));
+try
+{
+ $v->clean(array('start' => null, 'end' => date('Y-m-d', time() - (10*86400))));
+ $t->fail('clean() detects an unique past date');
+}
+catch (sfValidatorError $e)
+{
+ $t->pass('clean() detects an unique past date');
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+require_once dirname(__FILE__).'/../bootstrap.php';
+require_once dirname(__FILE__).'/../../lib/widget/sfWidgetFormReCaptcha.class.php';
+require_once $_SERVER['SYMFONY'].'/util/sfDomCssSelector.class.php';
+
+$t = new lime_test(7, new lime_output_color());
+
+$REMOTE_ADDR = 'symfony.example.com';
+$PUBLIC_KEY = '6Lf2DQEAAAAAALB9pGaVdcjMiv4CAuOVkfCSVvGh';
+$PRIVATE_KEY = '6Lf2DQEAAAAAALnEL0iEogIxZNYMlG7pmNhwEXjk';
+
+// __construct()
+$t->diag('__construct()');
+try
+{
+ new sfWidgetFormReCaptcha();
+}
+catch (RuntimeException $e)
+{
+ $t->pass('__construct() expects a "public_key" option');
+}
+
+// ->render()
+$t->diag('->render()');
+$w = new sfWidgetFormReCaptcha(array('public_key' => $PUBLIC_KEY));
+$dom = new DomDocument('1.0', 'utf-8');
+$dom->loadHTML($w->render('captcha'));
+$c = new sfDomCssSelector($dom);
+$t->is(count($c->matchSingle('script[src^="http://"]')->getNodes()), 1, '->render() uses the HTTP ReCaptcha URL by default');
+$t->is(count($c->matchSingle(sprintf('script[src$="%s"]', $PUBLIC_KEY))->getNodes()), 1, '->render() embeds the ReCatpcha public key in the URL');
+$t->is(count($c->matchSingle('iframe[src^="http://"]')->getNodes()), 1, '->render() uses the HTTP ReCaptcha URL by default');
+$t->is(count($c->matchSingle(sprintf('iframe[src$="%s"]', $PUBLIC_KEY))->getNodes()), 1, '->render() embeds the ReCatpcha public key in the URL');
+
+$w = new sfWidgetFormReCaptcha(array('public_key' => $PUBLIC_KEY, 'use_ssl' => true));
+$dom = new DomDocument('1.0', 'utf-8');
+$dom->loadHTML($w->render('captcha'));
+$c = new sfDomCssSelector($dom);
+$t->is(count($c->matchSingle('script[src^="https://"]')->getNodes()), 1, '->render() uses the HTTPS ReCaptcha URL is use_ssl is true');
+$t->is(count($c->matchSingle('iframe[src^="https://"]')->getNodes()), 1, '->render() uses the HTTPS ReCaptcha URL is use_ssl is true');
--- /dev/null
+.ac_results {
+ padding: 0px;
+ border: 1px solid black;
+ background-color: white;
+ overflow: hidden;
+ z-index: 99999;
+}
+
+.ac_results ul {
+ width: 100%;
+ list-style-position: outside;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.ac_results li {
+ margin: 0px;
+ padding: 2px 5px;
+ cursor: default;
+ display: block;
+ /*
+ if width will be 100% horizontal scrollbar will apear
+ when scroll mode will be used
+ */
+ /*width: 100%;*/
+ font: menu;
+ font-size: 12px;
+ /*
+ it is very important, if line-height not setted or setted
+ in relative units scroll will be broken in firefox
+ */
+ line-height: 16px;
+ overflow: hidden;
+}
+
+.ac_loading {
+ background: white url('../images/indicator.gif') right center no-repeat;
+}
+
+.ac_odd {
+ background-color: #eee;
+}
+
+.ac_over {
+ background-color: #0A246A;
+ color: white;
+}
--- /dev/null
+var sfDoubleList =
+{
+ init: function(id, className)
+ {
+ form = sfDoubleList.get_current_form(id);
+
+ callback = function() { sfDoubleList.submit(form, className) };
+
+ if (form.addEventListener)
+ {
+ form.addEventListener("submit", callback, false);
+ }
+ else if (form.attachEvent)
+ {
+ var r = form.attachEvent("onsubmit", callback);
+ }
+ },
+
+ move: function(srcId, destId)
+ {
+ var src = document.getElementById(srcId);
+ var dest = document.getElementById(destId);
+ for (var i = 0; i < src.options.length; i++)
+ {
+ if (src.options[i].selected)
+ {
+ dest.options[dest.length] = new Option(src.options[i].text, src.options[i].value);
+ src.options[i] = null;
+ --i;
+ }
+ }
+ },
+
+ submit: function(form, className)
+ {
+ var element;
+
+ for (var i = 0; i < form.elements.length; i++)
+ {
+ element = form.elements[i];
+ if (element.type == 'select-multiple')
+ {
+ if (element.className == className + '-selected')
+ {
+ for (var j = 0; j < element.options.length; j++)
+ {
+ element.options[j].selected = true;
+ }
+ }
+ }
+ }
+ },
+
+ get_current_form: function(el)
+ {
+ if ("form" != el.tagName.toLowerCase())
+ {
+ return sfDoubleList.get_current_form(el.parentNode);
+ }
+
+ return el;
+ }
+};
--- /dev/null
+/*
+ * jQuery Autocomplete plugin 1.1
+ *
+ * Copyright (c) 2009 Jörn Zaefferer
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
+ */
+
+;(function($) {
+
+$.fn.extend({
+ autocomplete: function(urlOrData, options) {
+ var isUrl = typeof urlOrData == "string";
+ options = $.extend({}, $.Autocompleter.defaults, {
+ url: isUrl ? urlOrData : null,
+ data: isUrl ? null : urlOrData,
+ delay: isUrl ? $.Autocompleter.defaults.delay : 10,
+ max: options && !options.scroll ? 10 : 150
+ }, options);
+
+ // if highlight is set to false, replace it with a do-nothing function
+ options.highlight = options.highlight || function(value) { return value; };
+
+ // if the formatMatch option is not specified, then use formatItem for backwards compatibility
+ options.formatMatch = options.formatMatch || options.formatItem;
+
+ return this.each(function() {
+ new $.Autocompleter(this, options);
+ });
+ },
+ result: function(handler) {
+ return this.bind("result", handler);
+ },
+ search: function(handler) {
+ return this.trigger("search", [handler]);
+ },
+ flushCache: function() {
+ return this.trigger("flushCache");
+ },
+ setOptions: function(options){
+ return this.trigger("setOptions", [options]);
+ },
+ unautocomplete: function() {
+ return this.trigger("unautocomplete");
+ }
+});
+
+$.Autocompleter = function(input, options) {
+
+ var KEY = {
+ UP: 38,
+ DOWN: 40,
+ DEL: 46,
+ TAB: 9,
+ RETURN: 13,
+ ESC: 27,
+ COMMA: 188,
+ PAGEUP: 33,
+ PAGEDOWN: 34,
+ BACKSPACE: 8
+ };
+
+ // Create $ object for input element
+ var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+
+ var timeout;
+ var previousValue = "";
+ var cache = $.Autocompleter.Cache(options);
+ var hasFocus = 0;
+ var lastKeyPressCode;
+ var config = {
+ mouseDownOnSelect: false
+ };
+ var select = $.Autocompleter.Select(options, input, selectCurrent, config);
+
+ var blockSubmit;
+
+ // prevent form submit in opera when selecting with return key
+ $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
+ if (blockSubmit) {
+ blockSubmit = false;
+ return false;
+ }
+ });
+
+ // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
+ $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
+ // a keypress means the input has focus
+ // avoids issue where input had focus before the autocomplete was applied
+ hasFocus = 1;
+ // track last key pressed
+ lastKeyPressCode = event.keyCode;
+ switch(event.keyCode) {
+
+ case KEY.UP:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.prev();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.DOWN:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.next();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.PAGEUP:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.pageUp();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ case KEY.PAGEDOWN:
+ event.preventDefault();
+ if ( select.visible() ) {
+ select.pageDown();
+ } else {
+ onChange(0, true);
+ }
+ break;
+
+ // matches also semicolon
+ case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
+ case KEY.TAB:
+ case KEY.RETURN:
+ if( selectCurrent() ) {
+ // stop default to prevent a form submit, Opera needs special handling
+ event.preventDefault();
+ blockSubmit = true;
+ return false;
+ }
+ break;
+
+ case KEY.ESC:
+ select.hide();
+ break;
+
+ default:
+ clearTimeout(timeout);
+ timeout = setTimeout(onChange, options.delay);
+ break;
+ }
+ }).focus(function(){
+ // track whether the field has focus, we shouldn't process any
+ // results if the field no longer has focus
+ hasFocus++;
+ }).blur(function() {
+ hasFocus = 0;
+ if (!config.mouseDownOnSelect) {
+ hideResults();
+ }
+ }).click(function() {
+ // show select when clicking in a focused field
+ if ( hasFocus++ > 1 && !select.visible() ) {
+ onChange(0, true);
+ }
+ }).bind("search", function() {
+ // TODO why not just specifying both arguments?
+ var fn = (arguments.length > 1) ? arguments[1] : null;
+ function findValueCallback(q, data) {
+ var result;
+ if( data && data.length ) {
+ for (var i=0; i < data.length; i++) {
+ if( data[i].result.toLowerCase() == q.toLowerCase() ) {
+ result = data[i];
+ break;
+ }
+ }
+ }
+ if( typeof fn == "function" ) fn(result);
+ else $input.trigger("result", result && [result.data, result.value]);
+ }
+ $.each(trimWords($input.val()), function(i, value) {
+ request(value, findValueCallback, findValueCallback);
+ });
+ }).bind("flushCache", function() {
+ cache.flush();
+ }).bind("setOptions", function() {
+ $.extend(options, arguments[1]);
+ // if we've updated the data, repopulate
+ if ( "data" in arguments[1] )
+ cache.populate();
+ }).bind("unautocomplete", function() {
+ select.unbind();
+ $input.unbind();
+ $(input.form).unbind(".autocomplete");
+ });
+
+
+ function selectCurrent() {
+ var selected = select.selected();
+ if( !selected )
+ return false;
+
+ var v = selected.result;
+ previousValue = v;
+
+ if ( options.multiple ) {
+ var words = trimWords($input.val());
+ if ( words.length > 1 ) {
+ var seperator = options.multipleSeparator.length;
+ var cursorAt = $(input).selection().start;
+ var wordAt, progress = 0;
+ $.each(words, function(i, word) {
+ progress += word.length;
+ if (cursorAt <= progress) {
+ wordAt = i;
+ return false;
+ }
+ progress += seperator;
+ });
+ words[wordAt] = v;
+ // TODO this should set the cursor to the right position, but it gets overriden somewhere
+ //$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
+ v = words.join( options.multipleSeparator );
+ }
+ v += options.multipleSeparator;
+ }
+
+ $input.val(v);
+ hideResultsNow();
+ $input.trigger("result", [selected.data, selected.value]);
+ return true;
+ }
+
+ function onChange(crap, skipPrevCheck) {
+ if( lastKeyPressCode == KEY.DEL ) {
+ select.hide();
+ return;
+ }
+
+ var currentValue = $input.val();
+
+ if ( !skipPrevCheck && currentValue == previousValue )
+ return;
+
+ previousValue = currentValue;
+
+ currentValue = lastWord(currentValue);
+ if ( currentValue.length >= options.minChars) {
+ $input.addClass(options.loadingClass);
+ if (!options.matchCase)
+ currentValue = currentValue.toLowerCase();
+ request(currentValue, receiveData, hideResultsNow);
+ } else {
+ stopLoading();
+ select.hide();
+ }
+ };
+
+ function trimWords(value) {
+ if (!value)
+ return [""];
+ if (!options.multiple)
+ return [$.trim(value)];
+ return $.map(value.split(options.multipleSeparator), function(word) {
+ return $.trim(value).length ? $.trim(word) : null;
+ });
+ }
+
+ function lastWord(value) {
+ if ( !options.multiple )
+ return value;
+ var words = trimWords(value);
+ if (words.length == 1)
+ return words[0];
+ var cursorAt = $(input).selection().start;
+ if (cursorAt == value.length) {
+ words = trimWords(value)
+ } else {
+ words = trimWords(value.replace(value.substring(cursorAt), ""));
+ }
+ return words[words.length - 1];
+ }
+
+ // fills in the input box w/the first match (assumed to be the best match)
+ // q: the term entered
+ // sValue: the first matching result
+ function autoFill(q, sValue){
+ // autofill in the complete box w/the first match as long as the user hasn't entered in more data
+ // if the last user key pressed was backspace, don't autofill
+ if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
+ // fill in the value (keep the case the user has typed)
+ $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
+ // select the portion of the value not typed by the user (so the next character will erase)
+ $(input).selection(previousValue.length, previousValue.length + sValue.length);
+ }
+ };
+
+ function hideResults() {
+ clearTimeout(timeout);
+ timeout = setTimeout(hideResultsNow, 200);
+ };
+
+ function hideResultsNow() {
+ var wasVisible = select.visible();
+ select.hide();
+ clearTimeout(timeout);
+ stopLoading();
+ if (options.mustMatch) {
+ // call search and run callback
+ $input.search(
+ function (result){
+ // if no value found, clear the input box
+ if( !result ) {
+ if (options.multiple) {
+ var words = trimWords($input.val()).slice(0, -1);
+ $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
+ }
+ else {
+ $input.val( "" );
+ $input.trigger("result", null);
+ }
+ }
+ }
+ );
+ }
+ };
+
+ function receiveData(q, data) {
+ if ( data && data.length && hasFocus ) {
+ stopLoading();
+ select.display(data, q);
+ autoFill(q, data[0].value);
+ select.show();
+ } else {
+ hideResultsNow();
+ }
+ };
+
+ function request(term, success, failure) {
+ if (!options.matchCase)
+ term = term.toLowerCase();
+ var data = cache.load(term);
+ // recieve the cached data
+ if (data && data.length) {
+ success(term, data);
+ // if an AJAX url has been supplied, try loading the data now
+ } else if( (typeof options.url == "string") && (options.url.length > 0) ){
+
+ var extraParams = {
+ timestamp: +new Date()
+ };
+ $.each(options.extraParams, function(key, param) {
+ extraParams[key] = typeof param == "function" ? param() : param;
+ });
+
+ $.ajax({
+ // try to leverage ajaxQueue plugin to abort previous requests
+ mode: "abort",
+ // limit abortion to this input
+ port: "autocomplete" + input.name,
+ dataType: options.dataType,
+ url: options.url,
+ data: $.extend({
+ q: lastWord(term),
+ limit: options.max
+ }, extraParams),
+ success: function(data) {
+ var parsed = options.parse && options.parse(data) || parse(data);
+ cache.add(term, parsed);
+ success(term, parsed);
+ }
+ });
+ } else {
+ // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
+ select.emptyList();
+ failure(term);
+ }
+ };
+
+ function parse(data) {
+ var parsed = [];
+ var rows = data.split("\n");
+ for (var i=0; i < rows.length; i++) {
+ var row = $.trim(rows[i]);
+ if (row) {
+ row = row.split("|");
+ parsed[parsed.length] = {
+ data: row,
+ value: row[0],
+ result: options.formatResult && options.formatResult(row, row[0]) || row[0]
+ };
+ }
+ }
+ return parsed;
+ };
+
+ function stopLoading() {
+ $input.removeClass(options.loadingClass);
+ };
+
+};
+
+$.Autocompleter.defaults = {
+ inputClass: "ac_input",
+ resultsClass: "ac_results",
+ loadingClass: "ac_loading",
+ minChars: 1,
+ delay: 400,
+ matchCase: false,
+ matchSubset: true,
+ matchContains: false,
+ cacheLength: 10,
+ max: 100,
+ mustMatch: false,
+ extraParams: {},
+ selectFirst: true,
+ formatItem: function(row) { return row[0]; },
+ formatMatch: null,
+ autoFill: false,
+ width: 0,
+ multiple: false,
+ multipleSeparator: ", ",
+ highlight: function(value, term) {
+ return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
+ },
+ scroll: true,
+ scrollHeight: 180
+};
+
+$.Autocompleter.Cache = function(options) {
+
+ var data = {};
+ var length = 0;
+
+ function matchSubset(s, sub) {
+ if (!options.matchCase)
+ s = s.toLowerCase();
+ var i = s.indexOf(sub);
+ if (options.matchContains == "word"){
+ i = s.toLowerCase().search("\\b" + sub.toLowerCase());
+ }
+ if (i == -1) return false;
+ return i == 0 || options.matchContains;
+ };
+
+ function add(q, value) {
+ if (length > options.cacheLength){
+ flush();
+ }
+ if (!data[q]){
+ length++;
+ }
+ data[q] = value;
+ }
+
+ function populate(){
+ if( !options.data ) return false;
+ // track the matches
+ var stMatchSets = {},
+ nullData = 0;
+
+ // no url was specified, we need to adjust the cache length to make sure it fits the local data store
+ if( !options.url ) options.cacheLength = 1;
+
+ // track all options for minChars = 0
+ stMatchSets[""] = [];
+
+ // loop through the array and create a lookup structure
+ for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
+ var rawValue = options.data[i];
+ // if rawValue is a string, make an array otherwise just reference the array
+ rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
+
+ var value = options.formatMatch(rawValue, i+1, options.data.length);
+ if ( value === false )
+ continue;
+
+ var firstChar = value.charAt(0).toLowerCase();
+ // if no lookup array for this character exists, look it up now
+ if( !stMatchSets[firstChar] )
+ stMatchSets[firstChar] = [];
+
+ // if the match is a string
+ var row = {
+ value: value,
+ data: rawValue,
+ result: options.formatResult && options.formatResult(rawValue) || value
+ };
+
+ // push the current match into the set list
+ stMatchSets[firstChar].push(row);
+
+ // keep track of minChars zero items
+ if ( nullData++ < options.max ) {
+ stMatchSets[""].push(row);
+ }
+ };
+
+ // add the data items to the cache
+ $.each(stMatchSets, function(i, value) {
+ // increase the cache size
+ options.cacheLength++;
+ // add to the cache
+ add(i, value);
+ });
+ }
+
+ // populate any existing data
+ setTimeout(populate, 25);
+
+ function flush(){
+ data = {};
+ length = 0;
+ }
+
+ return {
+ flush: flush,
+ add: add,
+ populate: populate,
+ load: function(q) {
+ if (!options.cacheLength || !length)
+ return null;
+ /*
+ * if dealing w/local data and matchContains than we must make sure
+ * to loop through all the data collections looking for matches
+ */
+ if( !options.url && options.matchContains ){
+ // track all matches
+ var csub = [];
+ // loop through all the data grids for matches
+ for( var k in data ){
+ // don't search through the stMatchSets[""] (minChars: 0) cache
+ // this prevents duplicates
+ if( k.length > 0 ){
+ var c = data[k];
+ $.each(c, function(i, x) {
+ // if we've got a match, add it to the array
+ if (matchSubset(x.value, q)) {
+ csub.push(x);
+ }
+ });
+ }
+ }
+ return csub;
+ } else
+ // if the exact item exists, use it
+ if (data[q]){
+ return data[q];
+ } else
+ if (options.matchSubset) {
+ for (var i = q.length - 1; i >= options.minChars; i--) {
+ var c = data[q.substr(0, i)];
+ if (c) {
+ var csub = [];
+ $.each(c, function(i, x) {
+ if (matchSubset(x.value, q)) {
+ csub[csub.length] = x;
+ }
+ });
+ return csub;
+ }
+ }
+ }
+ return null;
+ }
+ };
+};
+
+$.Autocompleter.Select = function (options, input, select, config) {
+ var CLASSES = {
+ ACTIVE: "ac_over"
+ };
+
+ var listItems,
+ active = -1,
+ data,
+ term = "",
+ needsInit = true,
+ element,
+ list;
+
+ // Create results
+ function init() {
+ if (!needsInit)
+ return;
+ element = $("<div/>")
+ .hide()
+ .addClass(options.resultsClass)
+ .css("position", "absolute")
+ .appendTo(document.body);
+
+ list = $("<ul/>").appendTo(element).mouseover( function(event) {
+ if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
+ active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
+ $(target(event)).addClass(CLASSES.ACTIVE);
+ }
+ }).click(function(event) {
+ $(target(event)).addClass(CLASSES.ACTIVE);
+ select();
+ // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
+ input.focus();
+ return false;
+ }).mousedown(function() {
+ config.mouseDownOnSelect = true;
+ }).mouseup(function() {
+ config.mouseDownOnSelect = false;
+ });
+
+ if( options.width > 0 )
+ element.css("width", options.width);
+
+ needsInit = false;
+ }
+
+ function target(event) {
+ var element = event.target;
+ while(element && element.tagName != "LI")
+ element = element.parentNode;
+ // more fun with IE, sometimes event.target is empty, just ignore it then
+ if(!element)
+ return [];
+ return element;
+ }
+
+ function moveSelect(step) {
+ listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
+ movePosition(step);
+ var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
+ if(options.scroll) {
+ var offset = 0;
+ listItems.slice(0, active).each(function() {
+ offset += this.offsetHeight;
+ });
+ if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
+ list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
+ } else if(offset < list.scrollTop()) {
+ list.scrollTop(offset);
+ }
+ }
+ };
+
+ function movePosition(step) {
+ active += step;
+ if (active < 0) {
+ active = listItems.size() - 1;
+ } else if (active >= listItems.size()) {
+ active = 0;
+ }
+ }
+
+ function limitNumberOfItems(available) {
+ return options.max && options.max < available
+ ? options.max
+ : available;
+ }
+
+ function fillList() {
+ list.empty();
+ var max = limitNumberOfItems(data.length);
+ for (var i=0; i < max; i++) {
+ if (!data[i])
+ continue;
+ var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
+ if ( formatted === false )
+ continue;
+ var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
+ $.data(li, "ac_data", data[i]);
+ }
+ listItems = list.find("li");
+ if ( options.selectFirst ) {
+ listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
+ active = 0;
+ }
+ // apply bgiframe if available
+ if ( $.fn.bgiframe )
+ list.bgiframe();
+ }
+
+ return {
+ display: function(d, q) {
+ init();
+ data = d;
+ term = q;
+ fillList();
+ },
+ next: function() {
+ moveSelect(1);
+ },
+ prev: function() {
+ moveSelect(-1);
+ },
+ pageUp: function() {
+ if (active != 0 && active - 8 < 0) {
+ moveSelect( -active );
+ } else {
+ moveSelect(-8);
+ }
+ },
+ pageDown: function() {
+ if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
+ moveSelect( listItems.size() - 1 - active );
+ } else {
+ moveSelect(8);
+ }
+ },
+ hide: function() {
+ element && element.hide();
+ listItems && listItems.removeClass(CLASSES.ACTIVE);
+ active = -1;
+ },
+ visible : function() {
+ return element && element.is(":visible");
+ },
+ current: function() {
+ return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
+ },
+ show: function() {
+ var offset = $(input).offset();
+ element.css({
+ width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
+ top: offset.top + input.offsetHeight,
+ left: offset.left
+ }).show();
+ if(options.scroll) {
+ list.scrollTop(0);
+ list.css({
+ maxHeight: options.scrollHeight,
+ overflow: 'auto'
+ });
+
+ if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
+ var listHeight = 0;
+ listItems.each(function() {
+ listHeight += this.offsetHeight;
+ });
+ var scrollbarsVisible = listHeight > options.scrollHeight;
+ list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
+ if (!scrollbarsVisible) {
+ // IE doesn't recalculate width when scrollbar disappears
+ listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
+ }
+ }
+
+ }
+ },
+ selected: function() {
+ var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
+ return selected && selected.length && $.data(selected[0], "ac_data");
+ },
+ emptyList: function (){
+ list && list.empty();
+ },
+ unbind: function() {
+ element && element.remove();
+ }
+ };
+};
+
+$.fn.selection = function(start, end) {
+ if (start !== undefined) {
+ return this.each(function() {
+ if( this.createTextRange ){
+ var selRange = this.createTextRange();
+ if (end === undefined || start == end) {
+ selRange.move("character", start);
+ selRange.select();
+ } else {
+ selRange.collapse(true);
+ selRange.moveStart("character", start);
+ selRange.moveEnd("character", end);
+ selRange.select();
+ }
+ } else if( this.setSelectionRange ){
+ this.setSelectionRange(start, end);
+ } else if( this.selectionStart ){
+ this.selectionStart = start;
+ this.selectionEnd = end;
+ }
+ });
+ }
+ var field = this[0];
+ if ( field.createTextRange ) {
+ var range = document.selection.createRange(),
+ orig = field.value,
+ teststring = "<->",
+ textLength = range.text.length;
+ range.text = teststring;
+ var caretAt = field.value.indexOf(teststring);
+ field.value = orig;
+ this.selection(caretAt, caretAt + textLength);
+ return {
+ start: caretAt,
+ end: caretAt + textLength
+ }
+ } else if( field.selectionStart !== undefined ){
+ return {
+ start: field.selectionStart,
+ end: field.selectionEnd
+ }
+ }
+};
+
+})(jQuery);