--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
+use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
+use Symfony\Component\Form\FormError;
+use Rapsys\UserBundle\Utils\Slugger;
+
+class DefaultController extends Controller {
+ public function loginAction(Request $request, AuthenticationUtils $authenticationUtils) {
+ //Get template
+ $template = $this->container->getParameter(($alias = $this->getAlias()).'.login.template');
+ //Get context
+ $context = $this->container->getParameter($alias.'.login.context');
+
+ //Create the form according to the FormType created previously.
+ //And give the proper parameters
+ $form = $this->createForm('Rapsys\UserBundle\Form\LoginType', null, array(
+ // To set the action use $this->generateUrl('route_identifier')
+ 'action' => $this->generateUrl('rapsys_user_login'),
+ 'method' => 'POST'
+ ));
+
+ //Get the login error if there is one
+ if ($error = $authenticationUtils->getLastAuthenticationError()) {
+ //Get translator
+ $trans = $this->get('translator');
+
+ //Get translated error
+ $error = $trans->trans($error->getMessageKey());
+
+ //Add error message to mail field
+ $form->get('mail')->addError(new FormError($error));
+ }
+
+ //Last username entered by the user
+ if ($lastUsername = $authenticationUtils->getLastUsername()) {
+ $form->get('mail')->setData($lastUsername);
+ }
+
+ //Render view
+ return $this->render($template, $context+array('form' => $form->createView(), 'error' => $error));
+ }
+
+ public function registerAction(Request $request, UserPasswordEncoderInterface $encoder) {
+ //Get mail template
+ $mailTemplate = $this->container->getParameter(($alias = $this->getAlias()).'.register.mail_template');
+ //Get mail context
+ $mailContext = $this->container->getParameter($alias.'.register.mail_context');
+ //Get template
+ $template = $this->container->getParameter($alias.'.register.template');
+ //Get context
+ $context = $this->container->getParameter($alias.'.register.context');
+ //Get home name
+ $homeName = $this->container->getParameter($alias.'.contact.home_name');
+ //Get home args
+ $homeArgs = $this->container->getParameter($alias.'.contact.home_args');
+ //Get contact name
+ $contactName = $this->container->getParameter($alias.'.contact.name');
+ //Get contact mail
+ $contactMail = $this->container->getParameter($alias.'.contact.mail');
+ //TODO: check if doctrine orm replacement is enough with default classes here
+ //Get class user
+ $classUser = $this->container->getParameter($alias.'.class.user');
+ //Get class group
+ $classGroup = $this->container->getParameter($alias.'.class.group');
+ //Get class title
+ $classTitle = $this->container->getParameter($alias.'.class.title');
+
+ //Create the form according to the FormType created previously.
+ //And give the proper parameters
+ $form = $this->createForm('Rapsys\UserBundle\Form\RegisterType', null, array(
+ // To set the action use $this->generateUrl('route_identifier')
+ 'class_title' => $classTitle,
+ 'action' => $this->generateUrl('rapsys_user_register'),
+ 'method' => 'POST'
+ ));
+
+ if ($request->isMethod('POST')) {
+ // Refill the fields in case the form is not valid.
+ $form->handleRequest($request);
+
+ if ($form->isValid()) {
+ //Get translator
+ $trans = $this->get('translator');
+
+ //Set data
+ $data = $form->getData();
+
+ //Translate title
+ $mailContext['title'] = $trans->trans($mailContext['title']);
+
+ //Translate title
+ $mailContext['subtitle'] = $trans->trans($mailContext['subtitle'], array('%name%' => $data['forename'].' '.$data['surname'].' ('.$data['pseudonym'].')'));
+
+ //Translate subject
+ $mailContext['subject'] = $trans->trans($mailContext['subject'], array('%title%' => $mailContext['title']));
+
+ //Translate message
+ $mailContext['message'] = $trans->trans($mailContext['message'], array('%title%' => $mailContext['title']));
+
+ //Create message
+ $message = \Swift_Message::newInstance()
+ ->setSubject($mailContext['subject'])
+ ->setFrom(array($contactMail => $contactName))
+ ->setTo(array($data['mail'] => $data['forename'].' '.$data['surname']))
+ ->setBody($mailContext['message'])
+ ->addPart(
+ $this->renderView(
+ $mailTemplate,
+ $mailContext+array(
+ 'home' => $this->get('router')->generate($homeName, $homeArgs, UrlGeneratorInterface::ABSOLUTE_URL)
+ )
+ ),
+ 'text/html'
+ );
+
+ //Get doctrine
+ $doctrine = $this->getDoctrine();
+
+ //Get manager
+ $manager = $doctrine->getManager();
+
+ //Init reflection
+ $reflection = new \ReflectionClass($classUser);
+
+ //Create new user
+ $user = $reflection->newInstance();
+
+ $user->setMail($data['mail']);
+ $user->setPseudonym($data['pseudonym']);
+ $user->setForename($data['forename']);
+ $user->setSurname($data['surname']);
+ $user->setPassword($encoder->encodePassword($user, $data['password']));
+ $user->setActive(true);
+ $user->setTitle($data['title']);
+ //TODO: see if we can't modify group constructor to set role directly from args
+ //XXX: see vendor/symfony/symfony/src/Symfony/Component/Security/Core/Role/Role.php
+ $user->addGroup($doctrine->getRepository($classGroup)->findOneByRole('ROLE_USER'));
+ $user->setCreated(new \DateTime('now'));
+ $user->setUpdated(new \DateTime('now'));
+
+ //Persist user
+ $manager->persist($user);
+
+ try {
+ //Send to database
+ $manager->flush();
+
+ //Send message
+ if ($this->get('mailer')->send($message)) {
+ //Redirect to cleanup the form
+ return $this->redirectToRoute('rapsys_user_register', array('sent' => 1));
+ }
+ } catch (\Doctrine\DBAL\Exception\UniqueConstraintViolationException $e) {
+ //Add error message mail already exists
+ $form->get('mail')->addError(new FormError($trans->trans('Account already exists: %mail%', array('%mail%' => $data['mail']))));
+ }
+ }
+ }
+
+ //Render view
+ return $this->render($template, $context+array('form' => $form->createView(), 'sent' => $request->query->get('sent', 0)));
+ }
+
+ public function recoverAction(Request $request, Slugger $slugger) {
+ //Get mail template
+ $mailTemplate = $this->container->getParameter(($alias = $this->getAlias()).'.recover.mail_template');
+ //Get mail context
+ $mailContext = $this->container->getParameter($alias.'.recover.mail_context');
+ //Get template
+ $template = $this->container->getParameter($alias.'.recover.template');
+ //Get context
+ $context = $this->container->getParameter($alias.'.recover.context');
+ //Get url name
+ $urlName = $this->container->getParameter($alias.'.recover.url_name');
+ //Get url args
+ $urlArgs = $this->container->getParameter($alias.'.recover.url_args');
+ //Get home name
+ $homeName = $this->container->getParameter($alias.'.contact.home_name');
+ //Get home args
+ $homeArgs = $this->container->getParameter($alias.'.contact.home_args');
+ //Get contact name
+ $contactName = $this->container->getParameter($alias.'.contact.name');
+ //Get contact mail
+ $contactMail = $this->container->getParameter($alias.'.contact.mail');
+ //Get class user
+ $classUser = $this->container->getParameter($alias.'.class.user');
+
+ //Create the form according to the FormType created previously.
+ //And give the proper parameters
+ $form = $this->createForm('Rapsys\UserBundle\Form\RecoverType', null, array(
+ // To set the action use $this->generateUrl('route_identifier')
+ 'action' => $this->generateUrl('rapsys_user_recover'),
+ 'method' => 'POST'
+ ));
+
+ if ($request->isMethod('POST')) {
+ // Refill the fields in case the form is not valid.
+ $form->handleRequest($request);
+
+ if ($form->isValid()) {
+ //Get translator
+ $trans = $this->get('translator');
+
+ //Get doctrine
+ $doctrine = $this->getDoctrine();
+
+ //Set data
+ $data = $form->getData();
+
+ //Translate title
+ $mailContext['title'] = $trans->trans($mailContext['title']);
+
+ //Try to find user
+ if ($user = $doctrine->getRepository($classUser)->findOneByMail($data['mail'])) {
+ //Translate title
+ $mailContext['subtitle'] = $trans->trans($mailContext['subtitle'], array('%name%' => $user->getForename().' '.$user->getSurname().' ('.$user->getPseudonym().')'));
+
+ //Translate subject
+ $mailContext['subject'] = $trans->trans($mailContext['subject'], array('%title%' => $mailContext['title']));
+
+ //Translate message
+ $mailContext['raw'] = $trans->trans($mailContext['raw'], array('%title%' => $mailContext['title'], '%url%' => $this->get('router')->generate($urlName, $urlArgs+array('mail' => $slugger->short($user->getMail()), 'hash' => $slugger->hash($user->getPassword())), UrlGeneratorInterface::ABSOLUTE_URL)));
+
+ //Create message
+ $message = \Swift_Message::newInstance()
+ ->setSubject($mailContext['subject'])
+ ->setFrom(array($contactMail => $contactName))
+ ->setTo(array($user->getMail() => $user->getForename().' '.$user->getSurname()))
+ ->setBody(strip_tags($mailContext['raw']))
+ ->addPart(
+ $this->renderView(
+ $mailTemplate,
+ $mailContext+array(
+ 'home' => $this->get('router')->generate($homeName, $homeArgs, UrlGeneratorInterface::ABSOLUTE_URL)
+ )
+ ),
+ 'text/html'
+ );
+
+ //Send message
+ if ($this->get('mailer')->send($message)) {
+ //Redirect to cleanup the form
+ return $this->redirectToRoute('rapsys_user_recover', array('sent' => 1));
+ }
+ //Accout not found
+ } else {
+ //Add error message to mail field
+ $form->get('mail')->addError(new FormError($trans->trans('Unable to find account: %mail%', array('%mail%' => $data['mail']))));
+ }
+ }
+ }
+
+ //Render view
+ return $this->render($template, $context+array('form' => $form->createView(), 'sent' => $request->query->get('sent', 0)));
+ }
+
+ public function recoverMailAction(Request $request, UserPasswordEncoderInterface $encoder, Slugger $slugger, $mail, $hash) {
+ //Get mail template
+ $mailTemplate = $this->container->getParameter(($alias = $this->getAlias()).'.recover_mail.mail_template');
+ //Get mail context
+ $mailContext = $this->container->getParameter($alias.'.recover_mail.mail_context');
+ //Get template
+ $template = $this->container->getParameter($alias.'.recover_mail.template');
+ //Get context
+ $context = $this->container->getParameter($alias.'.recover_mail.context');
+ //Get url name
+ $urlName = $this->container->getParameter($alias.'.recover_mail.url_name');
+ //Get url args
+ $urlArgs = $this->container->getParameter($alias.'.recover_mail.url_args');
+ //Get home name
+ $homeName = $this->container->getParameter($alias.'.contact.home_name');
+ //Get home args
+ $homeArgs = $this->container->getParameter($alias.'.contact.home_args');
+ //Get contact name
+ $contactName = $this->container->getParameter($alias.'.contact.name');
+ //Get contact mail
+ $contactMail = $this->container->getParameter($alias.'.contact.mail');
+ //Get class user
+ $classUser = $this->container->getParameter($alias.'.class.user');
+
+ //Create the form according to the FormType created previously.
+ //And give the proper parameters
+ $form = $this->createForm('Rapsys\UserBundle\Form\RecoverMailType', null, array(
+ // To set the action use $this->generateUrl('route_identifier')
+ 'action' => $this->generateUrl('rapsys_user_recover_mail', array('mail' => $mail, 'hash' => $hash)),
+ 'method' => 'POST'
+ ));
+
+ //Get doctrine
+ $doctrine = $this->getDoctrine();
+
+ //Get translator
+ $trans = $this->get('translator');
+
+ //Init not found
+ $notfound = 1;
+
+ //Retrieve user
+ if (($user = $doctrine->getRepository($classUser)->findOneByMail($slugger->unshort($mail))) && $hash == $slugger->hash($user->getPassword())) {
+ //User was found
+ $notfound = 0;
+
+ if ($request->isMethod('POST')) {
+ // Refill the fields in case the form is not valid.
+ $form->handleRequest($request);
+
+ if ($form->isValid()) {
+ //Set data
+ $data = $form->getData();
+
+ //Translate title
+ $mailContext['title'] = $trans->trans($mailContext['title']);
+
+ //Translate title
+ $mailContext['subtitle'] = $trans->trans($mailContext['subtitle'], array('%name%' => $user->getForename().' '.$user->getSurname().' ('.$user->getPseudonym().')'));
+
+ //Translate subject
+ $mailContext['subject'] = $trans->trans($mailContext['subject'], array('%title%' => $mailContext['title']));
+
+ //Set user password
+ $user->setPassword($encoder->encodePassword($user, $data['password']));
+
+ //Translate message
+ $mailContext['raw'] = $trans->trans($mailContext['raw'], array('%title%' => $mailContext['title'], '%url%' => $this->get('router')->generate($urlName, $urlArgs+array('mail' => $slugger->short($user->getMail()), 'hash' => $slugger->hash($user->getPassword())), UrlGeneratorInterface::ABSOLUTE_URL)));
+
+ //Get manager
+ $manager = $doctrine->getManager();
+
+ //Persist user
+ $manager->persist($user);
+
+ //Send to database
+ $manager->flush();
+
+ //Create message
+ $message = \Swift_Message::newInstance()
+ ->setSubject($mailContext['subject'])
+ ->setFrom(array($contactMail => $contactName))
+ ->setTo(array($user->getMail() => $user->getForename().' '.$user->getSurname()))
+ ->setBody(strip_tags($mailContext['raw']))
+ ->addPart(
+ $this->renderView(
+ $mailTemplate,
+ $mailContext+array(
+ 'home' => $this->get('router')->generate($homeName, $homeArgs, UrlGeneratorInterface::ABSOLUTE_URL)
+ )
+ ),
+ 'text/html'
+ );
+
+ //Send message
+ if ($this->get('mailer')->send($message)) {
+ //Redirect to cleanup the form
+ return $this->redirectToRoute('rapsys_user_recover_mail', array('mail' => $mail, 'hash' => $hash, 'sent' => 1));
+ }
+ }
+ }
+ }
+
+ //Render view
+ return $this->render($template, $context+array('form' => $form->createView(), 'sent' => $request->query->get('sent', 0), 'notfound' => $notfound));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAlias() {
+ return 'rapsys_user';
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\DependencyInjection;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+/**
+ * This is the class that validates and merges configuration from your app/config files.
+ *
+ * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
+ */
+class Configuration implements ConfigurationInterface {
+ /**
+ * {@inheritdoc}
+ */
+ public function getConfigTreeBuilder() {
+ //Set tree builder
+ $treeBuilder = new TreeBuilder();
+
+ //The bundle default values
+ $defaults = [
+ 'class' => [
+ 'group' => 'Rapsys\\UserBundle\\Entity\\Group',
+ 'title' => 'Rapsys\\UserBundle\\Entity\\Title',
+ 'user' => 'Rapsys\\UserBundle\\Entity\\User'
+ ],
+ 'contact' => [
+ 'name' => 'John Doe',
+ 'mail' => 'contact@example.com',
+ 'home_name' => 'rapsys_user_homepage',
+ 'home_args' => []
+ ],
+ 'login' => [
+ 'template' => '@@RapsysUser/security/login.html.twig',
+ 'context' => []
+ ],
+ 'register' => [
+ 'mail_template' => '@@RapsysUser/mail/register.html.twig',
+ 'mail_context' => [
+ 'title' => 'Title',
+ 'subtitle' => 'Hi, %%name%%',
+ 'subject' => 'Welcome to %%title%%',
+ 'message' => 'Thanks so much for joining us, from now on, you are part of %%title%%.'
+ ],
+ 'template' => '@@RapsysUser/security/register.html.twig',
+ 'context' => []
+ ],
+ 'recover' => [
+ 'mail_template' => '@@RapsysUser/mail/recover.html.twig',
+ 'mail_context' => [
+ 'title' => 'Title',
+ 'subtitle' => 'Hi, %%name%%',
+ 'subject' => 'Recover account on %%title%%',
+ 'raw' => 'Thanks so much for joining us, to recover your account you can follow this link: <a href="%%url%%">%%url%%</a>'
+ ],
+ 'url_name' => 'rapsys_user_recover_mail',
+ 'url_args' => [],
+ 'template' => '@@RapsysUser/security/recover.html.twig',
+ 'context' => []
+ ],
+ 'recover_mail' => [
+ 'mail_template' => '@@RapsysUser/mail/recover.html.twig',
+ 'mail_context' => [
+ 'title' => 'Title',
+ 'subtitle' => 'Hi, %%name%%',
+ 'subject' => 'Account recovered on %%title%%',
+ 'raw' => 'Your account password has been changed, to recover your account you can follow this link: <a href="%%url%%">%%url%%</a>'
+ ],
+ 'url_name' => 'rapsys_user_recover_mail',
+ 'url_args' => [],
+ 'template' => '@@RapsysUser/security/recover_mail.html.twig',
+ 'context' => []
+ ]
+ ];
+
+ //Here we define the parameters that are allowed to configure the bundle.
+ //TODO: see https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php for default value and description
+ //TODO: see http://symfony.com/doc/current/components/config/definition.html
+ //TODO: see fosuser DependencyInjection/Configuration.php
+ //XXX: use bin/console config:dump-reference to dump class infos
+
+ //Here we define the parameters that are allowed to configure the bundle.
+ $treeBuilder
+ //Parameters
+ ->root('parameters')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->arrayNode('rapsys_user')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->arrayNode('class')
+ ->isRequired()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('group')->isRequired()->defaultValue($defaults['class']['group'])->end()
+ ->scalarNode('title')->isRequired()->defaultValue($defaults['class']['title'])->end()
+ ->scalarNode('user')->isRequired()->defaultValue($defaults['class']['user'])->end()
+ ->end()
+ ->end()
+ ->arrayNode('contact')
+ ->isRequired()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('name')->isRequired()->defaultValue($defaults['contact']['name'])->end()
+ ->scalarNode('mail')->isRequired()->defaultValue($defaults['contact']['mail'])->end()
+ ->scalarNode('home_name')->isRequired()->defaultValue($defaults['contact']['home_name'])->end()
+ ->arrayNode('home_args')
+ ->isRequired()
+ ->treatNullLike($defaults['contact']['home_args'])
+ ->defaultValue($defaults['contact']['home_args'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('login')
+ ->isRequired()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('template')->isRequired()->defaultValue($defaults['login']['template'])->end()
+ ->arrayNode('context')
+ ->isRequired()
+ ->treatNullLike(array())
+ ->defaultValue($defaults['login']['context'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('register')
+ ->isRequired()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('mail_template')->isRequired()->defaultValue($defaults['register']['mail_template'])->end()
+ ->arrayNode('mail_context')
+ ->isRequired()
+ ->treatNullLike($defaults['register']['mail_context'])
+ ->defaultValue($defaults['register']['mail_context'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->scalarNode('template')->isRequired()->defaultValue($defaults['register']['template'])->end()
+ ->arrayNode('context')
+ ->isRequired()
+ ->treatNullLike($defaults['register']['context'])
+ ->defaultValue($defaults['register']['context'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('recover')
+ ->isRequired()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('mail_template')->isRequired()->defaultValue($defaults['recover']['mail_template'])->end()
+ ->arrayNode('mail_context')
+ ->isRequired()
+ ->treatNullLike($defaults['recover']['mail_context'])
+ ->defaultValue($defaults['recover']['mail_context'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->scalarNode('url_name')->isRequired()->defaultValue($defaults['recover']['url_name'])->end()
+ ->arrayNode('url_args')
+ ->isRequired()
+ ->treatNullLike($defaults['recover']['url_args'])
+ ->defaultValue($defaults['recover']['url_args'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->scalarNode('template')->isRequired()->defaultValue($defaults['recover']['template'])->end()
+ ->arrayNode('context')
+ ->isRequired()
+ ->treatNullLike(array())
+ ->defaultValue($defaults['recover']['context'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('recover_mail')
+ ->isRequired()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('mail_template')->isRequired()->defaultValue($defaults['recover']['mail_template'])->end()
+ ->arrayNode('mail_context')
+ ->isRequired()
+ ->treatNullLike($defaults['recover']['mail_context'])
+ ->defaultValue($defaults['recover']['mail_context'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->scalarNode('url_name')->isRequired()->defaultValue($defaults['recover']['url_name'])->end()
+ ->arrayNode('url_args')
+ ->isRequired()
+ ->treatNullLike($defaults['recover']['url_args'])
+ ->defaultValue($defaults['recover']['url_args'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->scalarNode('template')->isRequired()->defaultValue($defaults['recover']['template'])->end()
+ ->arrayNode('context')
+ ->isRequired()
+ ->treatNullLike(array())
+ ->defaultValue($defaults['recover']['context'])
+ ->scalarPrototype()->end()
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ->end();
+
+ return $treeBuilder;
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\Loader;
+
+/**
+ * This is the class that loads and manages your bundle configuration.
+ *
+ * @link http://symfony.com/doc/current/cookbook/bundles/extension.html
+ */
+class RapsysUserExtension extends Extension {
+ /**
+ * {@inheritdoc}
+ */
+ public function load(array $configs, ContainerBuilder $container) {
+ //Load configuration
+ $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+ $loader->load('services.yml');
+
+ //Load configuration
+ $configuration = $this->getConfiguration($configs, $container);
+ $config = $this->processConfiguration($configuration, $configs);
+
+ //Set default config in parameter
+ if (!$container->hasParameter($alias = $this->getAlias())) {
+ $container->setParameter($alias, $config[$alias]);
+ } else {
+ $config[$alias] = $container->getParameter($alias);
+ }
+
+ //Transform the two level tree in flat parameters
+ foreach($config[$alias] as $k => $v) {
+ foreach($v as $s => $d) {
+ //Set is as parameters
+ $container->setParameter($alias.'.'.$k.'.'.$s, $d);
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAlias() {
+ return 'rapsys_user';
+ }
+
+ /**
+ * The function that parses the array to flatten it into a one level depth array
+ *
+ * @param $array The config values array
+ * @param $path The current key path
+ * @param $depth The maxmium depth
+ * @param $sep The separator string
+ */
+ /*protected function flatten($array, $path, $depth = 10, $sep = '.') {
+ //Init res
+ $res = array();
+
+ //Pass through non hashed or empty array
+ if ($depth && is_array($array) && ($array === [] || array_keys($array) === range(0, count($array) - 1))) {
+ $res[$path] = $array;
+ //Flatten hashed array
+ } elseif ($depth && is_array($array)) {
+ foreach($array as $k => $v) {
+ $sub = $path ? $path.$sep.$k:$k;
+ $res += $this->flatten($v, $sub, $depth - 1, $sep);
+ }
+ //Pass scalar value directly
+ } else {
+ $res[$path] = $array;
+ }
+
+ //Return result
+ return $res;
+ }*/
+}
--- /dev/null
+<?php
+
+// src/Rapsys/UserBundle/Entity/Group.php
+namespace Rapsys\UserBundle\Entity;
+
+class Group extends \Symfony\Component\Security\Core\Role\Role {
+ /**
+ * @var integer
+ */
+ protected $id;
+
+ /**
+ * @var string
+ */
+ protected $role;
+
+ /**
+ * @var \DateTime
+ */
+ protected $created;
+
+ /**
+ * @var \DateTime
+ */
+ protected $updated;
+
+ /**
+ * @var \Doctrine\Common\Collections\Collection
+ */
+ protected $users;
+
+ /**
+ * Constructor
+ * @param string $role The role name
+ */
+ public function __construct($role) {
+ $this->role = (string) $role;
+ $this->users = new \Doctrine\Common\Collections\ArrayCollection();
+ }
+
+ /**
+ * Set role
+ *
+ * @param string $role
+ *
+ * @return User
+ */
+ public function setRole($role) {
+ $this->role = $role;
+
+ return $this;
+ }
+
+ /**
+ * Get role
+ *
+ * @return string
+ */
+ public function getRole() {
+ return $this->role;
+ }
+
+ /**
+ * Set created
+ *
+ * @param \DateTime $created
+ *
+ * @return User
+ */
+ public function setCreated($created) {
+ $this->created = $created;
+
+ return $this;
+ }
+
+ /**
+ * Get created
+ *
+ * @return \DateTime
+ */
+ public function getCreated() {
+ return $this->created;
+ }
+
+ /**
+ * Set updated
+ *
+ * @param \DateTime $updated
+ *
+ * @return User
+ */
+ public function setUpdated($updated) {
+ $this->updated = $updated;
+
+ return $this;
+ }
+
+ /**
+ * Get updated
+ *
+ * @return \DateTime
+ */
+ public function getUpdated() {
+ return $this->updated;
+ }
+
+ /**
+ * Add user
+ *
+ * @param \Rapsys\UserBundle\Entity\User $user
+ *
+ * @return Group
+ */
+ public function addUser(\Rapsys\UserBundle\Entity\User $user) {
+ $this->users[] = $user;
+
+ return $this;
+ }
+
+ /**
+ * Remove user
+ *
+ * @param \Rapsys\UserBundle\Entity\User $user
+ */
+ public function removeUser(\Rapsys\UserBundle\Entity\User $user) {
+ $this->users->removeElement($user);
+ }
+
+ /**
+ * Get users
+ *
+ * @return \Doctrine\Common\Collections\Collection
+ */
+ public function getUsers() {
+ return $this->users;
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\Entity;
+
+/**
+ * Title
+ */
+class Title {
+ /**
+ * @var integer
+ */
+ protected $id;
+
+ /**
+ * @var string
+ */
+ protected $short;
+
+ /**
+ * @var string
+ */
+ protected $title;
+
+ /**
+ * @var \DateTime
+ */
+ protected $created;
+
+ /**
+ * @var \DateTime
+ */
+ protected $updated;
+
+ /**
+ * @var \Doctrine\Common\Collections\Collection
+ */
+ protected $users;
+
+ /**
+ * Constructor
+ */
+ public function __construct() {
+ $this->users = new \Doctrine\Common\Collections\ArrayCollection();
+ }
+
+ /**
+ * Get id
+ *
+ * @return integer
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Set short
+ *
+ * @param string $short
+ *
+ * @return Title
+ */
+ public function setShort($short) {
+ $this->short = $short;
+
+ return $this;
+ }
+
+ /**
+ * Get short
+ *
+ * @return string
+ */
+ public function getShort() {
+ return $this->short;
+ }
+
+ /**
+ * Set title
+ *
+ * @param string $title
+ *
+ * @return Title
+ */
+ public function setTitle($title) {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ /**
+ * Get title
+ *
+ * @return string
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * Set created
+ *
+ * @param \DateTime $created
+ *
+ * @return Title
+ */
+ public function setCreated($created) {
+ $this->created = $created;
+
+ return $this;
+ }
+
+ /**
+ * Get created
+ *
+ * @return \DateTime
+ */
+ public function getCreated() {
+ return $this->created;
+ }
+
+ /**
+ * Set updated
+ *
+ * @param \DateTime $updated
+ *
+ * @return Title
+ */
+ public function setUpdated($updated) {
+ $this->updated = $updated;
+
+ return $this;
+ }
+
+ /**
+ * Get updated
+ *
+ * @return \DateTime
+ */
+ public function getUpdated() {
+ return $this->updated;
+ }
+
+ /**
+ * Add user
+ *
+ * @param \Rapsys\UserBundle\Entity\User $user
+ *
+ * @return Title
+ */
+ public function addUser(\Rapsys\UserBundle\Entity\User $user) {
+ $this->users[] = $user;
+
+ return $this;
+ }
+
+ /**
+ * Remove user
+ *
+ * @param \Rapsys\UserBundle\Entity\User $user
+ */
+ public function removeUser(\Rapsys\UserBundle\Entity\User $user) {
+ $this->users->removeElement($user);
+ }
+
+ /**
+ * Get users
+ *
+ * @return \Doctrine\Common\Collections\Collection
+ */
+ public function getUsers() {
+ return $this->users;
+ }
+}
--- /dev/null
+<?php
+
+// src/Rapsys/UserBundle/Entity/User.php
+namespace Rapsys\UserBundle\Entity;
+
+class User implements \Symfony\Component\Security\Core\User\AdvancedUserInterface, \Serializable {
+ /**
+ * @var integer
+ */
+ protected $id;
+
+ /**
+ * @var string
+ */
+ protected $mail;
+
+ /**
+ * @var string
+ */
+ protected $pseudonym;
+
+ /**
+ * @var string
+ */
+ protected $forename;
+
+ /**
+ * @var string
+ */
+ protected $surname;
+
+ /**
+ * @var string
+ */
+ protected $password;
+
+ /**
+ * @var bool
+ */
+ protected $active;
+
+ /**
+ * @var \DateTime
+ */
+ protected $created;
+
+ /**
+ * @var \DateTime
+ */
+ protected $updated;
+
+ /**
+ * @var \Rapsys\UserBundle\Entity\Title
+ */
+ protected $title;
+
+ /**
+ * @var \Doctrine\Common\Collections\Collection
+ */
+ protected $groups;
+
+ /**
+ * User constructor.
+ */
+ public function __construct() {
+ $this->active = false;
+ $this->groups = new \Doctrine\Common\Collections\ArrayCollection();
+ }
+
+ /**
+ * Get id
+ *
+ * @return integer
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * Set mail
+ *
+ * @param string $mail
+ *
+ * @return User
+ */
+ public function setMail($mail) {
+ $this->mail = $mail;
+
+ return $this;
+ }
+
+ /**
+ * Get mail
+ *
+ * @return string
+ */
+ public function getMail() {
+ return $this->mail;
+ }
+
+ /**
+ * Set pseudonym
+ *
+ * @param string $pseudonym
+ *
+ * @return User
+ */
+ public function setPseudonym($pseudonym) {
+ $this->pseudonym = $pseudonym;
+
+ return $this;
+ }
+
+ /**
+ * Get pseudonym
+ *
+ * @return string
+ */
+ public function getPseudonym() {
+ return $this->pseudonym;
+ }
+
+ /**
+ * Set forename
+ *
+ * @param string $forename
+ *
+ * @return User
+ */
+ public function setForename($forename) {
+ $this->forename = $forename;
+
+ return $this;
+ }
+
+ /**
+ * Get forename
+ *
+ * @return string
+ */
+ public function getForename() {
+ return $this->forename;
+ }
+
+ /**
+ * Set surname
+ *
+ * @param string $surname
+ *
+ * @return User
+ */
+ public function setSurname($surname) {
+ $this->surname = $surname;
+
+ return $this;
+ }
+
+ /**
+ * Get surname
+ *
+ * @return string
+ */
+ public function getSurname() {
+ return $this->surname;
+ }
+
+ /**
+ * Set password
+ *
+ * @param string $password
+ *
+ * @return User
+ */
+ public function setPassword($password) {
+ $this->password = $password;
+
+ return $this;
+ }
+
+ /**
+ * Get password
+ *
+ * @return string
+ */
+ public function getPassword() {
+ return $this->password;
+ }
+
+ /**
+ * Set active
+ *
+ * @param bool $active
+ *
+ * @return User
+ */
+ public function setActive($active) {
+ $this->active = $active;
+
+ return $this;
+ }
+
+ /**
+ * Get active
+ *
+ * @return bool
+ */
+ public function getActive() {
+ return $this->active;
+ }
+
+ /**
+ * Set created
+ *
+ * @param \DateTime $created
+ *
+ * @return User
+ */
+ public function setCreated($created) {
+ $this->created = $created;
+
+ return $this;
+ }
+
+ /**
+ * Get created
+ *
+ * @return \DateTime
+ */
+ public function getCreated() {
+ return $this->created;
+ }
+
+ /**
+ * Set updated
+ *
+ * @param \DateTime $updated
+ *
+ * @return User
+ */
+ public function setUpdated($updated) {
+ $this->updated = $updated;
+
+ return $this;
+ }
+
+ /**
+ * Get updated
+ *
+ * @return \DateTime
+ */
+ public function getUpdated() {
+ return $this->updated;
+ }
+
+ /**
+ * Set title
+ */
+ public function setTitle($title) {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ /**
+ * Get title
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * Add group
+ *
+ * @param \Rapsys\UserBundle\Entity\Group $group
+ *
+ * @return User
+ */
+ public function addGroup(\Rapsys\UserBundle\Entity\Group $group) {
+ $this->groups[] = $group;
+
+ return $this;
+ }
+
+ /**
+ * Remove group
+ *
+ * @param \Rapsys\UserBundle\Entity\Group $group
+ */
+ public function removeGroup(\Rapsys\UserBundle\Entity\Group $group) {
+ $this->groups->removeElement($group);
+ }
+
+ /**
+ * Get groups
+ *
+ * @return \Doctrine\Common\Collections\Collection
+ */
+ public function getGroups() {
+ return $this->groups;
+ }
+
+ public function getRoles() {
+ return $this->groups->toArray();
+ }
+
+ public function getSalt() {
+ //No salt required with bcrypt
+ return null;
+ }
+
+ public function getUsername() {
+ return $this->mail;
+ }
+
+ public function eraseCredentials() {
+ }
+
+ public function serialize() {
+ return serialize(array(
+ $this->id,
+ $this->mail,
+ $this->password,
+ $this->active,
+ $this->created,
+ $this->updated
+ ));
+ }
+
+ public function unserialize($serialized) {
+ list(
+ $this->id,
+ $this->mail,
+ $this->password,
+ $this->active,
+ $this->created,
+ $this->updated
+ ) = unserialize($serialized);
+ }
+
+ public function isAccountNonExpired() {
+ return true;
+ }
+
+ public function isAccountNonLocked() {
+ return true;
+ }
+
+ public function isCredentialsNonExpired() {
+ return true;
+ }
+
+ public function isEnabled() {
+ return $this->active;
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Form\Extension\Core\Type\PasswordType;
+use Symfony\Component\Form\Extension\Core\Type\EmailType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+class LoginType extends AbstractType {
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options) {
+ return $builder->add('mail', EmailType::class, array('attr' => array('placeholder' => 'Your mail address'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your mail')), new Email(array('message' => 'Your mail doesn\'t seems to be valid')))))
+ ->add('password', PasswordType::class, array('attr' => array('placeholder' => 'Your password'), 'constraints' => array(new NotBlank(array("message" => "Please provide your password")))))
+ ->add('submit', SubmitType::class, array('label' => 'Send', 'attr' => array('class' => 'submit')));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function configureOptions(OptionsResolver $resolver) {
+ $resolver->setDefaults(['error_bubbling' => true]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName() {
+ return 'rapsys_user_login';
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
+use Symfony\Component\Form\Extension\Core\Type\PasswordType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+class RecoverMailType extends AbstractType {
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options) {
+ return $builder->add('password', RepeatedType::class, array('type' => PasswordType::class, 'invalid_message' => 'The password and confirmation must match', 'first_options' => array('attr' => array('placeholder' => 'Your password'), 'label' => 'Password'), 'second_options' => array('attr' => array('placeholder' => 'Your password confirmation'), 'label' => 'Confirm password'), 'options' => array('constraints' => array(new NotBlank(array('message' => 'Please provide your password'))))))
+ ->add('submit', SubmitType::class, array('label' => 'Send', 'attr' => array('class' => 'submit')));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function configureOptions(OptionsResolver $resolver) {
+ $resolver->setDefaults(['error_bubbling' => true]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName() {
+ return 'rapsys_user_recover_mail';
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Form\Extension\Core\Type\PasswordType;
+use Symfony\Component\Form\Extension\Core\Type\EmailType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+class RecoverType extends AbstractType {
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options) {
+ return $builder->add('mail', EmailType::class, array('attr' => array('placeholder' => 'Your mail address'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your mail')), new Email(array('message' => 'Your mail doesn\'t seems to be valid')))))
+ ->add('submit', SubmitType::class, array('label' => 'Send', 'attr' => array('class' => 'submit')));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function configureOptions(OptionsResolver $resolver) {
+ $resolver->setDefaults(['error_bubbling' => true]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName() {
+ return 'rapsys_user_recover';
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+use Symfony\Component\Form\Extension\Core\Type\EmailType;
+use Symfony\Component\Form\Extension\Core\Type\PasswordType;
+use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+class RegisterType extends AbstractType {
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options) {
+ return $builder->add('mail', EmailType::class, array('attr' => array('placeholder' => 'Your mail address'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your mail')), new Email(array('message' => 'Your mail doesn\'t seems to be valid')))))
+ #'RapsysUserBundle:Title'
+ ->add('title', EntityType::class, array('class' => $options['class_title'], 'choice_label' => 'title', 'attr' => array('placeholder' => 'Your title'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your title')))))
+ ->add('pseudonym', TextType::class, array('attr' => array('placeholder' => 'Your pseudonym'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your pseudonym')))))
+ ->add('forename', TextType::class, array('attr' => array('placeholder' => 'Your forename'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your forename')))))
+ ->add('surname', TextType::class, array('attr' => array('placeholder' => 'Your surname'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your surname')))))
+ ->add('password', RepeatedType::class, array('type' => PasswordType::class, 'invalid_message' => 'The password and confirmation must match', 'first_options' => array('attr' => array('placeholder' => 'Your password'), 'label' => 'Password'), 'second_options' => array('attr' => array('placeholder' => 'Your password confirmation'), 'label' => 'Confirm password'), 'options' => array('constraints' => array(new NotBlank(array('message' => 'Please provide your password'))))))
+ ->add('submit', SubmitType::class, array('label' => 'Send', 'attr' => array('class' => 'submit')));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function configureOptions(OptionsResolver $resolver) {
+ $resolver->setDefaults(['error_bubbling' => true]);
+ $resolver->setRequired('class_title');
+ $resolver->setAllowedTypes('class_title', 'string');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName() {
+ return 'rapsys_user_register';
+ }
+}
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class RapsysUserBundle extends Bundle {}
--- /dev/null
+Rapsys\UserBundle\Entity\Group:
+ type: mappedSuperclass
+ id:
+ id:
+ type: integer
+ generator:
+ strategy: AUTO
+ options:
+ unsigned: true
+ fields:
+ role:
+ type: string
+ length: 20
+ created:
+ type: datetime
+ updated:
+ type: datetime
+# manyToMany:
+# users:
+# targetEntity: User
+# mappedBy: groups
--- /dev/null
+Rapsys\UserBundle\Entity\Title:
+ type: mappedSuperclass
+ id:
+ id:
+ type: integer
+ generator:
+ strategy: AUTO
+ options:
+ unsigned: true
+ fields:
+ short:
+ type: string
+ length: 4
+ title:
+ type: string
+ length: 16
+ created:
+ type: datetime
+ updated:
+ type: datetime
+# oneToMany:
+# users:
+# targetEntity: User
+# mappedBy: title
--- /dev/null
+Rapsys\UserBundle\Entity\User:
+ type: mappedSuperclass
+ id:
+ id:
+ type: integer
+ generator:
+ strategy: AUTO
+ options:
+ unsigned: true
+ fields:
+ mail:
+ type: string
+ unique: true
+ length: 254
+ pseudonym:
+ type: string
+ length: 32
+ forename:
+ type: string
+ length: 32
+ surname:
+ type: string
+ length: 32
+ password:
+ type: string
+ length: 60
+ active:
+ type: boolean
+ options:
+ default: true
+ created:
+ type: datetime
+ updated:
+ type: datetime
+ manyToOne:
+ title:
+ targetEntity: Title
+ inversedBy: users
+ manyToMany:
+ groups:
+ targetEntity: Group
+ inversedBy: users
+#see if usefull: https://stackoverflow.com/questions/34523699/how-to-extend-doctrine-entity-in-another-bundle
+# joinTable:
+# name: groups_users
--- /dev/null
+rapsys_user_login:
+ path: /login
+ defaults: { _controller: RapsysUserBundle:Default:login }
+
+rapsys_user_recover:
+ path: /recover
+ defaults: { _controller: RapsysUserBundle:Default:recover }
+
+rapsys_user_recover_mail:
+ path: /recover/{mail}/{hash}
+ defaults: { _controller: RapsysUserBundle:Default:recoverMail }
+
+rapsys_user_register:
+ path: /register
+ defaults: { _controller: RapsysUserBundle:Default:register }
+
+rapsys_user_logout:
+ path: /logout
+
+rapsys_user_homepage:
+ path: /
--- /dev/null
+services:
+ Rapsys\UserBundle\Controller\DefaultController:
+ tags: [ controller.service_arguments ]
+ Rapsys\UserBundle\Utils\Slugger:
+ arguments: [ "@service_container" ]
+# rapsys_user.example:
+# class: Rapsys\UserBundle\Example
+# arguments: ["@service_id", "plain_value", "%parameter%"]
--- /dev/null
+<?php
+
+namespace Rapsys\UserBundle\Utils;
+
+class Slugger {
+ //The secret parameter
+ private $secret;
+
+ //The offset reduced from secret
+ private $offset;
+
+ //Retrieve secret and set offset from reduction
+ public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container) {
+ //Set secret
+ $this->secret = $container->getParameter('secret');
+
+ //Init rev array
+ $rev = array_flip(array_merge(range('0', '9'), range('a', 'z'), range('A', 'Z'), range('!', '~')));
+
+ //Set offset
+ $this->offset = array_reduce(str_split($this->secret), function ($res, $a) use ($rev) { return $res += $rev[$a]; }, count($this->secret)) % count($rev);
+ }
+
+ //Short the string
+ public function short($string) {
+ //Return string
+ $ret = '';
+
+ //Alphabet
+ $alpha = array_merge(range('0', '9'), range('a', 'z'), range('A', 'Z'), range('!', '~'));
+
+ //Reverse alphabet
+ $rev = array_flip($alpha);
+
+ //Number characters
+ $count = count($alpha);
+
+ //Iterate on each character
+ foreach(str_split($string) as $c) {
+ if (isset($rev[$c]) && isset($alpha[($rev[$c]+$this->offset)%$count])) {
+ $ret .= $alpha[($rev[$c]+$this->offset)%$count];
+ }
+ }
+
+ //Send result
+ return str_replace(array('+','/'), array('-','_'), base64_encode($ret));
+ }
+
+ //Unshort the string
+ public function unshort($string) {
+ //Return string
+ $ret = '';
+
+ //Alphabet
+ $alpha = array_merge(range('0', '9'), range('a', 'z'), range('A', 'Z'), range('!', '~'));
+
+ //Reverse alphabet
+ $rev = array_flip($alpha);
+
+ //Number characters
+ $count = count($alpha);
+
+ //Iterate on each character
+ foreach(str_split(base64_decode(str_replace(array('-','_'), array('+','/'), $string))) as $c) {
+ if (isset($rev[$c]) && isset($alpha[($rev[$c]-$this->offset+$count)%$count])) {
+ $ret .= $alpha[($rev[$c]-$this->offset+$count)%$count];
+ }
+ }
+
+ //Send result
+ return $ret;
+ }
+
+ //Crypt and base64uri encode string
+ public function hash($string) {
+ return str_replace(array('+','/'), array('-','_'), base64_encode(crypt($string, $this->secret)));
+ }
+
+ //Convert string to safe slug
+ function slug($string) {
+ return preg_replace('/[\/_|+ -]+/', '-', strtolower(trim(preg_replace('/[^a-zA-Z0-9\/_|+ -]/', '', str_replace(array('\'', '"'), ' ', iconv('UTF-8', 'ASCII//TRANSLIT', $string))), '-')));
+ }
+
+}