From 0cd92dfa9fcfed4ba9ac5499d5c685062753d602 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 24 Sep 2012 10:53:36 +1000 Subject: [PATCH] Controllers Authentication was moved from the member controller the new AuthController. User entity created strictly for session management Symfony config Service and parameter configuration has been switched to YAML and consolidated into app/config/config.yml Removed Twig extension from the service container (services.xml) Presentation LABJS test added Some sample js added to test inline js vs callbacks vs Twig/Angular/etc Tests New units tests for Landing, Profile, Auth and Member controllers --- composer.json | 45 ++ src/BodyRep/Controller/AuthController.php | 68 +++ src/BodyRep/Controller/CommentController.php | 65 +++ src/BodyRep/Controller/LandingController.php | 1 + src/BodyRep/Controller/MemberController.php | 160 +++--- src/BodyRep/Controller/ProfileController.php | 12 +- .../DependencyInjection/BodyRepExtension.php | 8 +- src/BodyRep/Entity/Comment.php | 115 ++++ src/BodyRep/Entity/Member.php | 14 + src/BodyRep/Entity/User.php | 110 ++++ src/BodyRep/Entity/UserRepository.php | 15 + src/BodyRep/EventListener/CommentListener.php | 32 ++ .../EventListener/ControllerListener.php | 20 + src/BodyRep/EventListener/KernelListener.php | 20 + src/BodyRep/EventListener/LoginListener.php | 46 ++ .../Resources/views/Auth/login.html.twig | 34 ++ .../Resources/views/Auth/navbar.html.twig | 35 ++ .../views/Member/editProfile.html.twig | 2 +- .../Resources/views/Member/index.html.twig | 7 +- .../Resources/views/Member/profile.html.twig | 2 +- .../Resources/views/Profile/comment.html.twig | 12 + .../Resources/views/Profile/index.html.twig | 11 +- .../views/Profile/reputation.html.twig | 10 + src/BodyRep/Resources/views/layout.html.twig | 36 +- .../Tests/Controller/AuthControllerTest.php | 47 ++ .../Controller/LandingControllerTest.php | 19 + .../Tests/Controller/MemberControllerTest.php | 45 ++ .../Controller/ProfileControllerTest.php | 51 ++ web/js/LAB-debug.min.js | 5 + web/js/LAB.js | 5 + web/js/LAB.min.js | 5 + web/js/LAB.src.js | 514 ++++++++++++++++++ web/js/application.js | 8 +- 33 files changed, 1460 insertions(+), 119 deletions(-) create mode 100644 composer.json create mode 100644 src/BodyRep/Controller/AuthController.php create mode 100644 src/BodyRep/Controller/CommentController.php create mode 100644 src/BodyRep/Entity/Comment.php create mode 100644 src/BodyRep/Entity/User.php create mode 100644 src/BodyRep/Entity/UserRepository.php create mode 100644 src/BodyRep/EventListener/CommentListener.php create mode 100644 src/BodyRep/EventListener/KernelListener.php create mode 100644 src/BodyRep/EventListener/LoginListener.php create mode 100644 src/BodyRep/Resources/views/Auth/login.html.twig create mode 100644 src/BodyRep/Resources/views/Auth/navbar.html.twig create mode 100644 src/BodyRep/Resources/views/Profile/comment.html.twig create mode 100644 src/BodyRep/Resources/views/Profile/reputation.html.twig create mode 100644 src/BodyRep/Tests/Controller/AuthControllerTest.php create mode 100644 src/BodyRep/Tests/Controller/LandingControllerTest.php create mode 100644 src/BodyRep/Tests/Controller/MemberControllerTest.php create mode 100644 src/BodyRep/Tests/Controller/ProfileControllerTest.php create mode 100755 web/js/LAB-debug.min.js create mode 100755 web/js/LAB.js create mode 100755 web/js/LAB.min.js create mode 100755 web/js/LAB.src.js diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d5490c7 --- /dev/null +++ b/composer.json @@ -0,0 +1,45 @@ +{ + "name": "symfony/framework-standard-edition", + "description": "The \"Symfony Standard Edition\" distribution", + "autoload": { + "psr-0": { "": "src/" } + }, + "require": { + "php": ">=5.3.3", + "symfony/symfony": "2.1.*", + "doctrine/orm": ">=2.2.3,<2.4-dev", + "doctrine/doctrine-bundle": "1.0.*", + "twig/extensions": "1.0.*", + "symfony/assetic-bundle": "2.1.*", + "symfony/swiftmailer-bundle": "2.1.*", + "symfony/monolog-bundle": "2.1.*", + "sensio/distribution-bundle": "2.1.*", + "sensio/framework-extra-bundle": "2.1.*", + "sensio/generator-bundle": "2.1.*", + "jms/security-extra-bundle": "1.2.*", + "jms/di-extra-bundle": "1.1.*", + "knplabs/doctrine-behaviors": "dev-master" + }, + "scripts": { + "post-install-cmd": [ + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile" + ], + "post-update-cmd": [ + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile" + ] + }, + "config": { + "bin-dir": "bin" + }, + "minimum-stability": "dev", + "extra": { + "symfony-app-dir": "app", + "symfony-web-dir": "web" + } +} diff --git a/src/BodyRep/Controller/AuthController.php b/src/BodyRep/Controller/AuthController.php new file mode 100644 index 0000000..2795ee6 --- /dev/null +++ b/src/BodyRep/Controller/AuthController.php @@ -0,0 +1,68 @@ +generateUrl('_login')); + } + + /** + /** + * @Route("/login", name="_login") + * @Template() + */ + public function loginAction() + { + $request = $this->getRequest(); + $session = $request->getSession(); + + // get the login error if there is one + if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR); + } else { + $error = $session->get(SecurityContext::AUTHENTICATION_ERROR); + } + + return $this->render('BodyRep:Auth:login.html.twig', array( + // last username entered by the user + 'last_username' => $session->get(SecurityContext::LAST_USERNAME), + 'error' => $error, + )); + } + /** + * @Route("/login/check", name="_login_check") + * @Template() + */ + public function securityCheckAction() + { + // The security layer will intercept this request + } + /** + * @Route("/logout", name="_logout") + * @Template() + */ + public function logoutAction() + { + // The security layer will intercept this request + } + + public function navbarAction() + { + // The security layer will intercept this request + } +} \ No newline at end of file diff --git a/src/BodyRep/Controller/CommentController.php b/src/BodyRep/Controller/CommentController.php new file mode 100644 index 0000000..5e55ff7 --- /dev/null +++ b/src/BodyRep/Controller/CommentController.php @@ -0,0 +1,65 @@ +getUser()->getUsername() == $username) + return new RedirectResponse($this->generateUrl('_member_profile')); + + $db = $this->getDoctrine()->getManager(); + + $query = $db->createQuery(' + SELECT p + FROM BodyRep:Profile p + WHERE p.username = :username') + ->setParameter('username', $username) + ->setMaxResults(1); + if (sizeof($query->getResult()) != 1) + throw $this->createNotFoundException("User '".$username."' not found"); + + $profile = $query->getSingleResult(); + $username = $this->getUser()->getUsername(); + $db = $this->getDoctrine()->getManager(); + $query = $db->createQuery(' + SELECT m + FROM BodyRep:Member m + WHERE m.username = :username') + ->setParameter('username', $username) + ->setMaxResults(1); + + if (sizeof($query->getResult()) != 1) + throw $this->createNotFoundException("User '".$username."' not found"); + + $member = $query->getSingleResult(); + + return (array('sFullName' => $profile->getFullName(), 'name' => $member->getFullName())); + + } + /** + * @Route("/rep", name="_profile_reputation") + * @Template() + */ + public function reputationAction($username) + { + return $this->indexAction($username); + } + + +} diff --git a/src/BodyRep/Controller/LandingController.php b/src/BodyRep/Controller/LandingController.php index 8e64ec3..e72c501 100644 --- a/src/BodyRep/Controller/LandingController.php +++ b/src/BodyRep/Controller/LandingController.php @@ -22,6 +22,7 @@ class LandingController extends Controller * or @Template annotation as demonstrated in DemoController. * */ + return $this->render('BodyRep:Landing:index.html.twig'); } diff --git a/src/BodyRep/Controller/MemberController.php b/src/BodyRep/Controller/MemberController.php index 3e86708..be7d9db 100644 --- a/src/BodyRep/Controller/MemberController.php +++ b/src/BodyRep/Controller/MemberController.php @@ -1,50 +1,63 @@ get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { - $error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR); - } else { - $error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR); - } + parent::setContainer($container); - return array( - 'last_username' => $this->get('request')->getSession()->get(SecurityContext::LAST_USERNAME), - 'error' => $error, - ); + # Lookup member if logged in + $user = $this->getUser(); + + if (method_exists($user, 'getUsername')) + { + $db = $this->getDoctrine()->getManager(); + $username = $this->getUser()->getUsername(); + + $query = $db->createQuery(' + SELECT m + FROM BodyRep:Member m + WHERE m.username = :username') + ->setParameter('username', $username) + ->setMaxResults(1); + + if (sizeof($query->getResult()) != 1) + throw $this->createNotFoundException("User '".$username."' not found"); + + $this->member = $query->getSingleResult(); + } + else + trigger_error("Container cannot determine member information"); } - - /** - * @Route("/login_check", name="_security_check") - */ - public function securityCheckAction() + + + private function getMember() { - // The security layer will intercept this request + return $this->member; } - - /** - * @Route("/logout", name="_logout") - */ - public function logoutAction() + + public function navbarAction() { // The security layer will intercept this request } @@ -56,20 +69,9 @@ class MemberController extends Controller public function indexAction() { $username = $this->getUser()->getUsername(); - $db = $this->getDoctrine()->getManager(); - $query = $db->createQuery(' - SELECT m - FROM BodyRep:Member m - WHERE m.username = :username') - ->setParameter('username', $username) - ->setMaxResults(1); + - if (sizeof($query->getResult()) != 1) - throw $this->createNotFoundException("User '".$username."' not found"); - - $member = $query->getSingleResult(); - - return array('name' => $member->getFullName()); + return array('name' => $this->getMember()->getFullName()); } /** @@ -78,7 +80,7 @@ class MemberController extends Controller */ public function profileAction() { - $username = $this->getUser()->getUsername(); + $username = $this->getUser()->getUsername(); $db = $this->getDoctrine()->getManager(); $query = $db->createQuery(' @@ -91,22 +93,10 @@ class MemberController extends Controller throw $this->createNotFoundException("User '".$username."' not found"); $profile = $query->getSingleResult(); - $db = $this->getDoctrine()->getManager(); - $query = $db->createQuery(' - SELECT m - FROM BodyRep:Member m - WHERE m.username = :username') - ->setParameter('username', $username) - ->setMaxResults(1); - - if (sizeof($query->getResult()) != 1) - throw $this->createNotFoundException("User '".$username."' not found"); - - $member = $query->getSingleResult(); - - - return (array('sFullName' => $profile->getFullName(), 'name' => $member->getFullName())); + + return (array('sFullName' => $profile->getFullName(), 'name' => $this->getMember()->getFullName())); } + /** * @Route("/profile/edit", name="_member_profile_edit") * @Template() @@ -114,41 +104,19 @@ class MemberController extends Controller public function editProfileAction() { $username = $this->getUser()->getUsername(); - $db = $this->getDoctrine()->getManager(); - $query = $db->createQuery(' - SELECT m - FROM BodyRep:Member m - WHERE m.username = :username') - ->setParameter('username', $username) - ->setMaxResults(1); + $form = $this->get('form.factory')->create(new Profile(), array('fullname' => $this->getMember()->getFullName())); + $error = ''; - if (sizeof($query->getResult()) != 1) - throw $this->createNotFoundException("User '".$username."' not found"); - - $member = $query->getSingleResult(); - $form = $this->get('form.factory')->create(new Profile(), array('fullname' => $member->getFullName())); - $error = ''; - - return array('form' => $form->createView(), 'error' => ''); + return array('form' => $form->createView(), 'error' => ''); } - /** + + /** * @Route("/profile/save", name="_member_profile_save") */ public function saveAction() { $username = $this->getUser()->getUsername(); $db = $this->getDoctrine()->getManager(); - $query = $db->createQuery(' - SELECT m - FROM BodyRep:Member m - WHERE m.username = :username') - ->setParameter('username', $username) - ->setMaxResults(1); - - if (sizeof($query->getResult()) != 1) - throw $this->createNotFoundException("User '".$username."' not found"); - - $member = $query->getSingleResult(); $json = array('result' => 0); $form = $this->get('form.factory')->create(new Profile()); @@ -156,18 +124,30 @@ class MemberController extends Controller $form->bind($request); + $np1 = $request->get('_newpass1'); + $np2 = $request->get('_newpass2'); + // if (!empty($np1) && !$np1 != $np2) + // $form->get("fullname")->addError(new FormError('Passwords do not match')); - if ($form->isValid()) + if ($form->isValid() && sizeof($_POST) > 0) { $json['result'] = 1; + if (!empty($np1)) + { + $factory = $this->get('security.encoder_factory'); + $encoder = $factory->getEncoder($this->getUser()); + $password = $encoder->encodePassword($np1, $this->getUser()->getSalt()); + + $this->member->setPassword($password); + } $d = $form->getClientData(); - $member->setFullName($d['fullname']); - $db->persist($member); + $this->getMember()->setFullName($d['fullname']); + $db->persist($this->getMember()); $db->flush(); } $resp = new Response (json_encode($json)); - $resp->headers->set('Content-Type', 'text/plain'); + $resp->headers->set('Content-Type', 'application/json'); return $resp; } @@ -179,7 +159,7 @@ class MemberController extends Controller { /* - * Integreted suggester response + * Integrated suggester response * */ $em = $this->getDoctrine()->getManager(); @@ -208,7 +188,5 @@ class MemberController extends Controller } else*/ return array('search' => $res); - - } } diff --git a/src/BodyRep/Controller/ProfileController.php b/src/BodyRep/Controller/ProfileController.php index 662b6c5..f60a462 100644 --- a/src/BodyRep/Controller/ProfileController.php +++ b/src/BodyRep/Controller/ProfileController.php @@ -14,11 +14,12 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class ProfileController extends Controller { /** - * @Route("/{username}", name="_profile") + * @Route("/", name="_profile") * @Template() */ public function indexAction($username) { + if ($this->getUser()->getUsername() == $username) return new RedirectResponse($this->generateUrl('_member_profile')); @@ -48,10 +49,17 @@ class ProfileController extends Controller $member = $query->getSingleResult(); - return (array('sFullName' => $profile->getFullName(), 'name' => $member->getFullName())); } + /** + * @Route("/rep", name="_profile_reputation") + * @Template() + */ + public function reputationAction($username) + { + return $this->indexAction($username); + } } diff --git a/src/BodyRep/DependencyInjection/BodyRepExtension.php b/src/BodyRep/DependencyInjection/BodyRepExtension.php index 92bd84b..52c0661 100644 --- a/src/BodyRep/DependencyInjection/BodyRepExtension.php +++ b/src/BodyRep/DependencyInjection/BodyRepExtension.php @@ -3,16 +3,16 @@ namespace BodyRep\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\Config\FileLocator; +#use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +#use Symfony\Component\Config\FileLocator; class BodyRepExtension extends Extension { public function load(array $configs, ContainerBuilder $container) { - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('services.xml'); + #$loader = new YamlFileLoader($container, new FileLocator(__DIR__)); + #$loader->load('/data/www/br/src/BodyRep/Resources/config/services.yml'); } public function getAlias() diff --git a/src/BodyRep/Entity/Comment.php b/src/BodyRep/Entity/Comment.php new file mode 100644 index 0000000..5c56dc3 --- /dev/null +++ b/src/BodyRep/Entity/Comment.php @@ -0,0 +1,115 @@ +id; + } + + /** + * Set author + * + * @param string $author + * @return Comment + */ + public function setAuthor($author) + { + $this->author = $author; + + return $this; + } + + /** + * Get author + * + * @return string + */ + public function getAuthor() + { + return $this->author; + } + + /** + * Set text + * + * @param string $text + * @return Comment + */ + public function setText($text) + { + $this->text = $text; + + return $this; + } + + /** + * Get text + * + * @return string + */ + public function getText() + { + return $this->text; + } + + /** + * Set created + * + * @param timestamp $created + * @return Comment + */ + public function setCreated(\timestamp $created) + { + $this->created = $created; + + return $this; + } + + /** + * Get created + * + * @return timestamp + */ + public function getCreated() + { + return $this->created; + } +} \ No newline at end of file diff --git a/src/BodyRep/Entity/Member.php b/src/BodyRep/Entity/Member.php index 29f13f8..83c0dce 100644 --- a/src/BodyRep/Entity/Member.php +++ b/src/BodyRep/Entity/Member.php @@ -114,6 +114,20 @@ class Member implements UserInterface return $this; } + /** + * Set password + * + * @param string $password + * @return Member + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** * Get fullName * diff --git a/src/BodyRep/Entity/User.php b/src/BodyRep/Entity/User.php new file mode 100644 index 0000000..66e8cba --- /dev/null +++ b/src/BodyRep/Entity/User.php @@ -0,0 +1,110 @@ +roles; + } + + public function getSalt() + { + return $this->salt; + } + + public function getUsername() + { + return $this->username; + } + + public function eraseCredentials() + { + } + + public function getPassword() + { + return $this->password; + } + + /** + * Get id + * + * @return integer + */ + public function getId() + { + return $this->id; + } + + /** + * Set username + * + * @param string $username + * @return User + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Set password + * + * @param string $password + * @return User + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Set salt + * + * @param string $salt + * @return User + */ + public function setSalt($salt) + { + $this->salt = $salt; + + return $this; + } +} \ No newline at end of file diff --git a/src/BodyRep/Entity/UserRepository.php b/src/BodyRep/Entity/UserRepository.php new file mode 100644 index 0000000..5e55cfd --- /dev/null +++ b/src/BodyRep/Entity/UserRepository.php @@ -0,0 +1,15 @@ +mailer = $mailer; + } + + public function onCommentEvent(CommentEvent $event) + { + $post = $event->getPost(); + $comment = $event->getComment(); + + foreach ($post->getSubscribers() as $subscriber) { + $message = Swift_Message::newInstance() + ->setSubject('New comment posted on ' . $post->getTitle()) + ->setFrom('send@example.com') + ->setTo($subscriber->getEmail()) + ->setBody("Hey, somebody left a new comment on a post you're subscribed to! It says: " . $comment->getBody()) + ; + $this->mailer->send($message); + } + } +} + +?> \ No newline at end of file diff --git a/src/BodyRep/EventListener/ControllerListener.php b/src/BodyRep/EventListener/ControllerListener.php index 41b6f92..a4f81fb 100644 --- a/src/BodyRep/EventListener/ControllerListener.php +++ b/src/BodyRep/EventListener/ControllerListener.php @@ -23,4 +23,24 @@ class ControllerListener $this->extension->setController($event->getController()); } } + public function preExecute(\Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event){ + //result returned by the controller + $data = $event->getControllerResult(); + + //Get the current route + $route = $event->getRequest()->get('_route'); + + /* @var $request \Symfony\Component\HttpFoundation\Request */ + $request = $event->getRequest(); + $template = $request->get('_template'); + $route = $request->get('_route'); + + if(substr($route,0,7) == 'mobile_'){ + $newTemplate = str_replace('html.twig','mobile.html.twig',$template); + + //Overwrite original template with the mobile one + $response = $this->templating->renderResponse($newTemplate, $data); + $event->setResponse($response); + } + } } diff --git a/src/BodyRep/EventListener/KernelListener.php b/src/BodyRep/EventListener/KernelListener.php new file mode 100644 index 0000000..f8f4989 --- /dev/null +++ b/src/BodyRep/EventListener/KernelListener.php @@ -0,0 +1,20 @@ +public function preExecute(\Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event){ + //result returned by the controller + $data = $event->getControllerResult(); + + //Get the current route + $route = $event->getRequest()->get('_route'); + + /* @var $request \Symfony\Component\HttpFoundation\Request */ + $request = $event->getRequest(); + $template = $request->get('_template'); + $route = $request->get('_route'); + + if(substr($route,0,7) == 'mobile_'){ + $newTemplate = str_replace('html.twig','mobile.html.twig',$template); + + //Overwrite original template with the mobile one + $response = $this->templating->renderResponse($newTemplate, $data); + $event->setResponse($response); + } +} \ No newline at end of file diff --git a/src/BodyRep/EventListener/LoginListener.php b/src/BodyRep/EventListener/LoginListener.php new file mode 100644 index 0000000..840f60a --- /dev/null +++ b/src/BodyRep/EventListener/LoginListener.php @@ -0,0 +1,46 @@ +context = $context; + $this->em = $doctrine->getEntityManager(); + } + + /** + * Do the magic. + * + * @param Event $event + */ + public function onSecurityInteractiveLogin(Event $event) + { + $user = $this->context->getToken()->getUser(); + + // do all your magic here + } +} + +?> \ No newline at end of file diff --git a/src/BodyRep/Resources/views/Auth/login.html.twig b/src/BodyRep/Resources/views/Auth/login.html.twig new file mode 100644 index 0000000..907d51c --- /dev/null +++ b/src/BodyRep/Resources/views/Auth/login.html.twig @@ -0,0 +1,34 @@ +{% extends 'BodyRep::layout.html.twig' %} +{% block js %} + $(document).ready(function() + { + if($('#username').val().length > 0) + $('#password').focus(); + else + $('#username').focus(); + }); +{% endblock %} + +{% block content %} + + +

Login

+ + {% if error %} +
{{ error.message }}
+ {% endif %} + +
+
+ + +
+ +
+ + +
+ + +
+{% endblock %} diff --git a/src/BodyRep/Resources/views/Auth/navbar.html.twig b/src/BodyRep/Resources/views/Auth/navbar.html.twig new file mode 100644 index 0000000..8324718 --- /dev/null +++ b/src/BodyRep/Resources/views/Auth/navbar.html.twig @@ -0,0 +1,35 @@ + diff --git a/src/BodyRep/Resources/views/Member/editProfile.html.twig b/src/BodyRep/Resources/views/Member/editProfile.html.twig index 7fbe7f6..e112e45 100644 --- a/src/BodyRep/Resources/views/Member/editProfile.html.twig +++ b/src/BodyRep/Resources/views/Member/editProfile.html.twig @@ -20,6 +20,6 @@
 
{{ form_rest(form) }} - + {% endblock %} diff --git a/src/BodyRep/Resources/views/Member/index.html.twig b/src/BodyRep/Resources/views/Member/index.html.twig index ab6f5fc..07126f2 100644 --- a/src/BodyRep/Resources/views/Member/index.html.twig +++ b/src/BodyRep/Resources/views/Member/index.html.twig @@ -3,10 +3,11 @@ {% block title "Hello " ~ name %} {% block content %} -{% include 'BodyRep:Member:navbar.html.twig' %} - +{% include 'BodyRep:Auth:navbar.html.twig' %} +

Member landing page

-{{name}} +

{{name}}

+
{% endblock %} diff --git a/src/BodyRep/Resources/views/Member/profile.html.twig b/src/BodyRep/Resources/views/Member/profile.html.twig index fc48237..75a5f7b 100644 --- a/src/BodyRep/Resources/views/Member/profile.html.twig +++ b/src/BodyRep/Resources/views/Member/profile.html.twig @@ -5,7 +5,7 @@ {% block content_header '' %} {% block content %} -{% include 'BodyRep:Member:navbar.html.twig' %} +{% include 'BodyRep:Auth:navbar.html.twig' %}
diff --git a/src/BodyRep/Resources/views/Profile/comment.html.twig b/src/BodyRep/Resources/views/Profile/comment.html.twig new file mode 100644 index 0000000..5b1cf96 --- /dev/null +++ b/src/BodyRep/Resources/views/Profile/comment.html.twig @@ -0,0 +1,12 @@ + {% for comment in comments %} +
+
+ {{ comment.author }} +

{{ comment.text }}

+ +
+ {% endfor %} \ No newline at end of file diff --git a/src/BodyRep/Resources/views/Profile/index.html.twig b/src/BodyRep/Resources/views/Profile/index.html.twig index 0f0c2b7..6d57614 100644 --- a/src/BodyRep/Resources/views/Profile/index.html.twig +++ b/src/BodyRep/Resources/views/Profile/index.html.twig @@ -1,11 +1,18 @@ {% extends 'BodyRep::layout.html.twig' %} {% block title %}BodyRep{% endblock %} +{% block documentReady %} +$('#rep').live('click', function() { + $.get('{{ path("_profile_reputation", { 'username': app.request.get('username') }) }}', {}, function (data) { + $('#mcnt').html(data); + }); +}); +{% endblock %} {% block content_header '' %} {% block content %} -{% include 'BodyRep:Member:navbar.html.twig' %} +{% include 'BodyRep:Auth:navbar.html.twig' %}
@@ -13,7 +20,7 @@
+
Loading
diff --git a/src/BodyRep/Tests/Controller/AuthControllerTest.php b/src/BodyRep/Tests/Controller/AuthControllerTest.php new file mode 100644 index 0000000..101e45d --- /dev/null +++ b/src/BodyRep/Tests/Controller/AuthControllerTest.php @@ -0,0 +1,47 @@ +followRedirects(true); + $crawler = $client->request('GET', '/u/login'); + + $this->assertTrue($crawler->filter('html:contains("Username")')->count() > 0); + } + + public function testLogin() + { + $client = static::createClient(); + $client->followRedirects(true); + $crawler = $client->request('GET', '/u/login'); + + + $form = $crawler->selectButton('loginbtn')->form(); + $crawler = $client->submit($form, array( + '_username' => 'alexl', + '_password' => 'd559ko54' + )); + + $this->assertTrue($crawler->filter('html:contains("Member")')->count() > 0); + } + + public function testLogout() + { + $client = static::createClient(); + $client->followRedirects(true); + + $crawler = $client->request('GET', '/u/logout'); + + $this->assertTrue($crawler->filter('html:contains("Landing")')->count() > 0); + } + +} +?> diff --git a/src/BodyRep/Tests/Controller/LandingControllerTest.php b/src/BodyRep/Tests/Controller/LandingControllerTest.php new file mode 100644 index 0000000..ab7feb5 --- /dev/null +++ b/src/BodyRep/Tests/Controller/LandingControllerTest.php @@ -0,0 +1,19 @@ +request('GET', '/'); + + $this->assertTrue($crawler->filter('html:contains("Landing Page")')->count() > 0); + } + +} +?> diff --git a/src/BodyRep/Tests/Controller/MemberControllerTest.php b/src/BodyRep/Tests/Controller/MemberControllerTest.php new file mode 100644 index 0000000..5e78b68 --- /dev/null +++ b/src/BodyRep/Tests/Controller/MemberControllerTest.php @@ -0,0 +1,45 @@ +authclient = static::createClient(); + $this->authclient->followRedirects(true); + $this->authcrawler = $this->authclient->request('GET', '/u/login'); + + + $form = $this->authcrawler->selectButton('loginbtn')->form(); + $this->authcrawler = $this->authclient->submit($form, array( + '_username' => 'alexl', + '_password' => 'd559ko54' + )); + + } + + public function testIndex() + { + $this->assertTrue($this->authcrawler->filter('html:contains("Member landing")')->count() > 0); + + } + + public function testEditProfile() + { + + + $this->authcrawler = $this->authclient->request('GET', '/m/profile'); + + $this->assertTrue($this->authcrawler->filter('html:contains("Edit Profile")')->count() > 0); + + } + +} +?> diff --git a/src/BodyRep/Tests/Controller/ProfileControllerTest.php b/src/BodyRep/Tests/Controller/ProfileControllerTest.php new file mode 100644 index 0000000..d78d81c --- /dev/null +++ b/src/BodyRep/Tests/Controller/ProfileControllerTest.php @@ -0,0 +1,51 @@ +followRedirects(true); + $crawler = $client->request('GET', '/u/login'); + + + $form = $crawler->selectButton('loginbtn')->form(); + $crawler = $client->submit($form, array( + '_username' => 'alexl', + '_password' => 'd559ko54' + )); + + + $crawler = $client->request('GET', '/alexl'); + + $this->assertTrue($crawler->filter('html:contains("Alex")')->count() > 0); + + } + + public function testEditProfile() + { + $client = static::createClient(); + $client->followRedirects(true); + $crawler = $client->request('GET', '/u/login'); + + + $form = $crawler->selectButton('loginbtn')->form(); + $crawler = $client->submit($form, array( + '_username' => 'alexl', + '_password' => 'd559ko54' + )); + + + $crawler = $client->request('GET', '/alexl'); + + $this->assertTrue($crawler->filter('html:contains("Alex")')->count() > 0); + + } + +} +?> diff --git a/web/js/LAB-debug.min.js b/web/js/LAB-debug.min.js new file mode 100755 index 0000000..99e7b14 --- /dev/null +++ b/web/js/LAB-debug.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(j){var N=j.$LAB,A="UseLocalXHR",B="AlwaysPreserveOrder",w="AllowDuplicates",C="CacheBust",l="Debug",D="BasePath",E=/^[^?#]*\//.exec(location.href)[0],F=/^\w+\:\/\/\/?[^\/]+/.exec(E)[0],i=document.head||document.getElementsByTagName("head"),O=(j.opera&&Object.prototype.toString.call(j.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),m=function(){},G=m,s=document.createElement("script"),H=typeof s.preload=="boolean",t=H||(s.readyState&&s.readyState=="uninitialized"),I=!t&&s.async===true,P=!t&&!I&&!O;if(j.console&&j.console.log){if(!j.console.error)j.console.error=j.console.log;m=function(a){j.console.log(a)};G=function(a,c){j.console.error(a,c)}}function J(a){return Object.prototype.toString.call(a)=="[object Function]"}function K(a){return Object.prototype.toString.call(a)=="[object Array]"}function Q(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?F:E)+a)}function u(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function R(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=q.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){j.$LAB=N;return p},sandbox:function(){return M()}};return p}j.$LAB=M();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/web/js/LAB.js b/web/js/LAB.js new file mode 100755 index 0000000..e710dfe --- /dev/null +++ b/web/js/LAB.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/web/js/LAB.min.js b/web/js/LAB.min.js new file mode 100755 index 0000000..e710dfe --- /dev/null +++ b/web/js/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/web/js/LAB.src.js b/web/js/LAB.src.js new file mode 100755 index 0000000..99807cd --- /dev/null +++ b/web/js/LAB.src.js @@ -0,0 +1,514 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ + +(function(global){ + var _$LAB = global.$LAB, + + // constants for the valid keys of the options object + _UseLocalXHR = "UseLocalXHR", + _AlwaysPreserveOrder = "AlwaysPreserveOrder", + _AllowDuplicates = "AllowDuplicates", + _CacheBust = "CacheBust", + /*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/ + _BasePath = "BasePath", + + // stateless variables used across all $LAB instances + root_page = /^[^?#]*\//.exec(location.href)[0], + root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0], + append_to = document.head || document.getElementsByTagName("head"), + + // inferences... ick, but still necessary + opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style), + +/*!START_DEBUG*/ + // console.log() and console.error() wrappers + log_msg = function(){}, + log_error = log_msg, +/*!END_DEBUG*/ + + // feature sniffs (yay!) + test_script_elem = document.createElement("script"), + explicit_preloading = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29 + real_preloading = explicit_preloading || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized"), // will a script preload with `src` set before DOM append? + script_ordered_async = !real_preloading && test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order + + // XHR preloading (same-domain) and cache-preloading (remote-domain) are the fallbacks (for some browsers) + xhr_or_cache_preloading = !real_preloading && !script_ordered_async && !opera_or_gecko + ; + +/*!START_DEBUG*/ + // define console wrapper functions if applicable + if (global.console && global.console.log) { + if (!global.console.error) global.console.error = global.console.log; + log_msg = function(msg) { global.console.log(msg); }; + log_error = function(msg,err) { global.console.error(msg,err); }; + } +/*!END_DEBUG*/ + + // test for function + function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; } + + // test for array + function is_array(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; } + + // make script URL absolute/canonical + function canonical_uri(src,base_path) { + var absolute_regex = /^\w+\:\/\//; + + // is `src` is protocol-relative (begins with // or ///), prepend protocol + if (/^\/\/\/?/.test(src)) { + src = location.protocol + src; + } + // is `src` page-relative? (not an absolute URL, and not a domain-relative path, beginning with /) + else if (!absolute_regex.test(src) && src.charAt(0) != "/") { + // prepend `base_path`, if any + src = (base_path || "") + src; + } + // make sure to return `src` as absolute + return absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src); + } + + // merge `source` into `target` + function merge_objs(source,target) { + for (var k in source) { if (source.hasOwnProperty(k)) { + target[k] = source[k]; // TODO: does this need to be recursive for our purposes? + }} + return target; + } + + // does the chain group have any ready-to-execute scripts? + function check_chain_group_scripts_ready(chain_group) { + var any_scripts_ready = false; + for (var i=0; i 0) { + for (var i=0; i=0;) { + val = queue.shift(); + $L = $L[val.type].apply(null,val.args); + } + return $L; + }, + + // rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB + noConflict:function(){ + global.$LAB = _$LAB; + return instanceAPI; + }, + + // create another clean instance of $LAB + sandbox:function(){ + return create_sandbox(); + } + }; + + return instanceAPI; + } + + // create the main instance of $LAB + global.$LAB = create_sandbox(); + + + /* The following "hack" was suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html + NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?). + + The hack essentially "patches" the **page** that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does + proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked + document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. + For instance, jQuery 1.4+ has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or + fixed by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs. + */ + (function(addEvent,domLoaded,handler){ + if (document.readyState == null && document[addEvent]){ + document.readyState = "loading"; + document[addEvent](domLoaded,handler = function(){ + document.removeEventListener(domLoaded,handler,false); + document.readyState = "complete"; + },false); + } + })("addEventListener","DOMContentLoaded"); + +})(this); \ No newline at end of file diff --git a/web/js/application.js b/web/js/application.js index 22ea34c..982306f 100644 --- a/web/js/application.js +++ b/web/js/application.js @@ -9,17 +9,17 @@ $(function() { frm = $(this); var searchTerm = frm.find('input').val(); url = frm.attr('action') + '/'+searchTerm; - $.get(url, {}, function(data) { + $.get(url, {}, function(data) { $('.symfony-content > .row-fluid').html(data); }); return false; }); $('form#profile').live('submit', function(){ frm = $(this); - $.post(frm.attr('action'), frm.serialize(), function(data) { - response = $.parseJSON(data); + $.post(frm.attr('action'), frm.serialize(), function(response) { + if(response.result) - $('#mcnt').append('Updated'); + $('#mcnt').append('Updated'); }); return false; });