├── .gitignore ├── .travis.yml ├── Controller └── VoteController.php ├── DependencyInjection ├── Configuration.php └── MsalsasVotingExtension.php ├── Entity ├── Click.php ├── ClickRepository.php ├── ReferenceClicks.php ├── ReferenceClicksRepository.php ├── ReferenceVotes.php ├── ReferenceVotesRepository.php ├── Vote │ └── AbstractVote.php ├── VoteNegative.php ├── VotePositive.php └── VoteRepository.php ├── LICENSE ├── MsalsasVotingBundle.php ├── README.md ├── Resources ├── config │ ├── doctrine │ │ ├── Click.orm.xml │ │ ├── ReferenceClicks.orm.xml │ │ ├── ReferenceVotes.orm.xml │ │ ├── VoteNegative.orm.xml │ │ └── VotePositive.orm.xml │ ├── routing │ │ └── msalsas_voting.xml │ └── services.xml ├── doc │ ├── clicks_or_views.rst │ ├── configuration_reference.rst │ ├── index.rst │ ├── publish.rst │ └── screenshot-1.png ├── public │ ├── css │ │ └── msalsas_voting_styles.css │ └── js │ │ ├── msalsas_voting_bottomBar.js │ │ └── msalsas_voting_shakeIt.js ├── translations │ ├── messages.en.yml │ └── messages.es.yml └── views │ └── msalsas_voting_widget.html.twig ├── Service ├── Clicker.php └── Voter.php ├── Tests ├── DependencyInjection │ └── MsalsasVotingExtensionTest.php ├── Entity │ ├── ClickTest.php │ ├── ReferenceClicksTest.php │ ├── ReferenceVotesTest.php │ ├── VoteNegativeTest.php │ └── VotePositiveTest.php ├── Mock │ ├── AnonymousUserMock.php │ ├── ClickMock.php │ ├── UserMock.php │ ├── VoteNegativeMock.php │ └── VotePositiveMock.php ├── Service │ ├── AbstractServiceTest.php │ ├── ClickerTest.php │ └── VoterTest.php └── bootstrap.php ├── composer.json └── phpunit.xml.dist /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | vendor -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | cache: 4 | directories: 5 | - $HOME/.composer/cache/files 6 | - $HOME/symfony-bridge/.phpunit 7 | 8 | env: 9 | global: 10 | - PHPUNIT_FLAGS="-v" 11 | - SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit" 12 | 13 | matrix: 14 | fast_finish: true 15 | include: 16 | # Minimum supported dependencies with the latest and oldest PHP version 17 | - php: 7.2 18 | env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors" 19 | 20 | # Test the latest stable release 21 | - php: 7.1 22 | - php: 7.2 23 | env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" 24 | 25 | # Latest commit to master 26 | - php: 7.2 27 | env: STABILITY="dev" 28 | 29 | allow_failures: 30 | # Dev-master is allowed to fail. 31 | - env: STABILITY="dev" 32 | 33 | before_install: 34 | - if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi 35 | - if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; fi; 36 | - if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi; 37 | 38 | install: 39 | # To be removed when this issue will be resolved: https://github.com/composer/composer/issues/5355 40 | - if [[ "$COMPOSER_FLAGS" == *"--prefer-lowest"* ]]; then composer update --prefer-dist --no-interaction --prefer-stable --quiet; fi 41 | - composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction 42 | - ./vendor/bin/simple-phpunit install 43 | 44 | script: 45 | - composer validate --strict --no-check-lock 46 | # simple-phpunit is the PHPUnit wrapper provided by the PHPUnit Bridge component and 47 | # it helps with testing legacy code and deprecations (composer require symfony/phpunit-bridge) 48 | - ./vendor/bin/simple-phpunit $PHPUNIT_FLAGS -------------------------------------------------------------------------------- /Controller/VoteController.php: -------------------------------------------------------------------------------- 1 | voter = $voter; 29 | } 30 | 31 | public function votePositive($id, Request $request) 32 | { 33 | if (!$request->isXmlHttpRequest()) { 34 | throw new AccessDeniedException(); 35 | } 36 | 37 | try { 38 | $votesCount = $this->voter->votePositive($id); 39 | } catch (AccessDeniedException $e) { 40 | return $this->json($e->getMessage(), 403); 41 | } catch (\Exception $e) { 42 | return $this->json($e->getMessage(), 403); 43 | } 44 | 45 | return $this->json($votesCount); 46 | } 47 | 48 | public function voteNegative($id, Request $request) 49 | { 50 | if (!$request->isXmlHttpRequest()) { 51 | throw new AccessDeniedException(); 52 | } 53 | 54 | $reason = $request->getContent(); 55 | if (!$reason || !is_string($reason) || !in_array($reason, $this->voter->getNegativeReasons())) { 56 | throw new AccessDeniedException(); 57 | } 58 | 59 | try { 60 | $votesCount = $this->voter->voteNegative($id, $reason); 61 | } catch (AccessDeniedException $e) { 62 | return $this->json($e->getMessage(), 403); 63 | } catch (\Exception $e) { 64 | return $this->json($e->getMessage(), 403); 65 | } 66 | 67 | return $this->json($votesCount); 68 | } 69 | } -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | root('msalsas_voting'); 29 | $rootNode->children() 30 | ->scalarNode('user_provider') 31 | ->isRequired() 32 | ->defaultValue('\App\Entity\User') 33 | ->end() 34 | ->arrayNode('negative_reasons') 35 | ->isRequired() 36 | ->scalarPrototype() 37 | ->defaultValue([ 38 | 'msalsas_voting.negative_reasons.irrelevant', 39 | 'msalsas_voting.negative_reasons.old', 40 | 'msalsas_voting.negative_reasons.tiredness', 41 | 'msalsas_voting.negative_reasons.sensationalist', 42 | 'msalsas_voting.negative_reasons.spam', 43 | 'msalsas_voting.negative_reasons.duplicated', 44 | 'msalsas_voting.negative_reasons.microblogging', 45 | 'msalsas_voting.negative_reasons.erroneous', 46 | 'msalsas_voting.negative_reasons.plagiarism', 47 | ]) 48 | ->end() 49 | ->end() 50 | ->integerNode('anonymous_percent_allowed') 51 | ->isRequired() 52 | ->defaultValue(2) 53 | ->min(1) 54 | ->end() 55 | ->integerNode('anonymous_min_allowed') 56 | ->isRequired() 57 | ->defaultValue(50) 58 | ->min(1) 59 | ->end() 60 | ->end(); 61 | 62 | return $builder; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /DependencyInjection/MsalsasVotingExtension.php: -------------------------------------------------------------------------------- 1 | load('services.xml'); 27 | $config = $this->processConfiguration(new Configuration(), $configs); 28 | $container->setParameter('msalsas_voting.negative_reasons', $config['negative_reasons']); 29 | $container->setParameter('msalsas_voting.anonymous_percent_allowed', $config['anonymous_percent_allowed']); 30 | $container->setParameter('msalsas_voting.anonymous_min_allowed', $config['anonymous_min_allowed']); 31 | } 32 | 33 | public function prepend(ContainerBuilder $container) 34 | { 35 | $configs = $container->getExtensionConfig($this->getAlias()); 36 | $config = $this->processConfiguration(new Configuration(), $configs); 37 | 38 | $doctrineConfig = []; 39 | $doctrineConfig['orm']['resolve_target_entities']['Msalsas\VotingBundle\Entity\Vote\UserInterface'] = $config['user_provider']; 40 | $doctrineConfig['orm']['mappings'][] = array( 41 | 'name' => 'MsalsasVotingBundle', 42 | 'is_bundle' => true, 43 | 'type' => 'xml', 44 | 'prefix' => 'Msalsas\VotingBundle\Entity' 45 | ); 46 | $container->prependExtensionConfig('doctrine', $doctrineConfig); 47 | 48 | $twigConfig = []; 49 | $twigConfig['globals']['msalsas_voting_voter'] = "@msalsas_voting.voter"; 50 | $twigConfig['globals']['msalsas_voting_clicker'] = "@msalsas_voting.clicker"; 51 | $twigConfig['paths'][__DIR__.'/../Resources/views'] = "msalsas_voting"; 52 | $twigConfig['paths'][__DIR__.'/../Resources/public'] = "msalsas_voting.public"; 53 | $container->prependExtensionConfig('twig', $twigConfig); 54 | } 55 | 56 | public function getAlias() 57 | { 58 | return 'msalsas_voting'; 59 | } 60 | } -------------------------------------------------------------------------------- /Entity/Click.php: -------------------------------------------------------------------------------- 1 | id; 45 | } 46 | 47 | /** 48 | * @param int $id 49 | */ 50 | public function setId(int $id) 51 | { 52 | $this->id = $id; 53 | } 54 | 55 | /** 56 | * @return null|UserInterface 57 | */ 58 | public function getUser() 59 | { 60 | return $this->user; 61 | } 62 | 63 | /** 64 | * @param null|UserInterface $user 65 | */ 66 | public function setUser($user) 67 | { 68 | $this->user = $user; 69 | } 70 | 71 | /** 72 | * @return int 73 | */ 74 | public function getReference(): int 75 | { 76 | return $this->reference; 77 | } 78 | 79 | /** 80 | * @param int $reference 81 | */ 82 | public function setReference(int $reference) 83 | { 84 | $this->reference = $reference; 85 | } 86 | 87 | /** 88 | * @return string 89 | */ 90 | public function getUserIP(): string 91 | { 92 | return $this->userIP; 93 | } 94 | 95 | /** 96 | * @param string $userIP 97 | */ 98 | public function setUserIP(string $userIP) 99 | { 100 | $this->userIP = $userIP; 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /Entity/ClickRepository.php: -------------------------------------------------------------------------------- 1 | reference; 33 | } 34 | 35 | /** 36 | * @param int $reference 37 | */ 38 | public function setReference(int $reference) 39 | { 40 | $this->reference = $reference; 41 | } 42 | 43 | /** 44 | * @return int 45 | */ 46 | public function getClicks(): int 47 | { 48 | return $this->clicks; 49 | } 50 | 51 | /** 52 | * @param int $clicks 53 | */ 54 | public function setClicks(int $clicks) 55 | { 56 | $this->clicks = $clicks; 57 | } 58 | 59 | public function addClick() 60 | { 61 | $this->clicks++; 62 | } 63 | } -------------------------------------------------------------------------------- /Entity/ReferenceClicksRepository.php: -------------------------------------------------------------------------------- 1 | reference; 53 | } 54 | 55 | /** 56 | * @param int $reference 57 | */ 58 | public function setReference(int $reference) 59 | { 60 | $this->reference = $reference; 61 | } 62 | 63 | /** 64 | * @return int 65 | */ 66 | public function getPositiveVotes(): int 67 | { 68 | return $this->positiveVotes; 69 | } 70 | 71 | /** 72 | * @param int $positiveVotes 73 | */ 74 | public function setPositiveVotes(int $positiveVotes) 75 | { 76 | $this->positiveVotes = $positiveVotes; 77 | } 78 | 79 | /** 80 | * @return int 81 | */ 82 | public function getNegativeVotes(): int 83 | { 84 | return $this->negativeVotes; 85 | } 86 | 87 | /** 88 | * @param int $negativeVotes 89 | */ 90 | public function setNegativeVotes(int $negativeVotes) 91 | { 92 | $this->negativeVotes = $negativeVotes; 93 | } 94 | 95 | /** 96 | * @return int 97 | */ 98 | public function getUserVotes(): int 99 | { 100 | return $this->userVotes; 101 | } 102 | 103 | /** 104 | * @param int $userVotes 105 | */ 106 | public function setUserVotes(int $userVotes) 107 | { 108 | $this->userVotes = $userVotes; 109 | } 110 | 111 | /** 112 | * @return int 113 | */ 114 | public function getAnonymousVotes(): int 115 | { 116 | return $this->anonymousVotes; 117 | } 118 | 119 | /** 120 | * @param int $anonymousVotes 121 | */ 122 | public function setAnonymousVotes(int $anonymousVotes) 123 | { 124 | $this->anonymousVotes = $anonymousVotes; 125 | } 126 | 127 | /** 128 | * @param boolean $positive 129 | * @param boolean $anonymous 130 | */ 131 | public function addVote(bool $positive, $anonymous = true) 132 | { 133 | if ($positive) { 134 | $this->positiveVotes++; 135 | if ($anonymous) { 136 | $this->anonymousVotes++; 137 | } else { 138 | $this->userVotes++; 139 | } 140 | } else { 141 | $this->negativeVotes++; 142 | } 143 | } 144 | 145 | /** 146 | * @return boolean 147 | */ 148 | public function isPublished(): bool 149 | { 150 | return $this->published; 151 | } 152 | 153 | /** 154 | * @param boolean $published 155 | */ 156 | public function setPublished(bool $published) 157 | { 158 | $this->published = $published; 159 | } 160 | } -------------------------------------------------------------------------------- /Entity/ReferenceVotesRepository.php: -------------------------------------------------------------------------------- 1 | id; 45 | } 46 | 47 | /** 48 | * @param int $id 49 | */ 50 | public function setId(int $id) 51 | { 52 | $this->id = $id; 53 | } 54 | 55 | /** 56 | * @return string|UserInterface 57 | */ 58 | public function getUser() 59 | { 60 | return $this->user; 61 | } 62 | 63 | /** 64 | * @param string|UserInterface $user 65 | */ 66 | public function setUser($user) 67 | { 68 | $this->user = $user; 69 | } 70 | 71 | /** 72 | * @return int 73 | */ 74 | public function getReference(): int 75 | { 76 | return $this->reference; 77 | } 78 | 79 | /** 80 | * @param int $reference 81 | */ 82 | public function setReference(int $reference) 83 | { 84 | $this->reference = $reference; 85 | } 86 | 87 | /** 88 | * @return string|null 89 | */ 90 | public function getUserIP(): string 91 | { 92 | return $this->userIP; 93 | } 94 | 95 | /** 96 | * @param string|null $userIP 97 | */ 98 | public function setUserIP(string $userIP = null) 99 | { 100 | $this->userIP = $userIP; 101 | } 102 | 103 | public function isPositive() 104 | { 105 | if (get_class($this) === 'Msalsas\VotingBundle\Entity\VotePositive') { 106 | return true; 107 | } 108 | 109 | return false; 110 | } 111 | } -------------------------------------------------------------------------------- /Entity/VoteNegative.php: -------------------------------------------------------------------------------- 1 | reason; 30 | } 31 | 32 | /** 33 | * @param string $reason 34 | */ 35 | public function setReason(string $reason) 36 | { 37 | $this->reason = $reason; 38 | } 39 | } -------------------------------------------------------------------------------- /Entity/VotePositive.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Resources/config/doctrine/ReferenceClicks.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Resources/config/doctrine/ReferenceVotes.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Resources/config/doctrine/VoteNegative.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Resources/config/doctrine/VotePositive.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Resources/config/routing/msalsas_voting.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | \d+ 9 | msalsas_voting.controller.vote_positive 10 | 11 | 12 | \d+ 13 | msalsas_voting.controller.vote_negative 14 | 15 | -------------------------------------------------------------------------------- /Resources/config/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | %msalsas_voting.anonymous_percent_allowed% 20 | %msalsas_voting.anonymous_min_allowed% 21 | %msalsas_voting.negative_reasons% 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Resources/doc/clicks_or_views.rst: -------------------------------------------------------------------------------- 1 | MsalsasVotingBundle clicks or views 2 | =================================== 3 | 4 | For incrementing the clicks or views of a reference you have to type-hint the ``Clicker`` in a 5 | controller action and call ``addClick`` with the reference id (you will have to configure this route): 6 | 7 | 8 | .. code-block:: php 9 | 10 | addClick($post->getId()); 15 | 16 | // ... 17 | } 18 | 19 | The ``addClick`` method will check if the user has already clicked. If it is an anonymous 20 | user, it will check for the client IP. 21 | 22 | If you want to make use of views instead of clicks, just override the clicks translation 23 | and call ``$clicker->addClick($post->getId())`` on the post view controller action. 24 | 25 | 26 | .. code-block:: php 27 | 28 | addClick($post->getId()); 33 | 34 | // ... 35 | } -------------------------------------------------------------------------------- /Resources/doc/configuration_reference.rst: -------------------------------------------------------------------------------- 1 | MsalsasVotingBundle Configuration Reference 2 | =========================================== 3 | 4 | All available configuration options are listed below with their default values. 5 | 6 | .. code-block:: yaml 7 | 8 | msalsas_voting: 9 | user_provider: \App\Entity\User 10 | negative_reasons: 11 | - msalsas_voting.negative_reasons.irrelevant 12 | - msalsas_voting.negative_reasons.old 13 | - msalsas_voting.negative_reasons.tiredness 14 | - msalsas_voting.negative_reasons.sensationalist 15 | - msalsas_voting.negative_reasons.spam 16 | - msalsas_voting.negative_reasons.duplicated 17 | - msalsas_voting.negative_reasons.microblogging 18 | - msalsas_voting.negative_reasons.erroneous 19 | - msalsas_voting.negative_reasons.plagiarism 20 | anonymous_percent_allowed: 50 21 | anonymous_min_allowed: 2 -------------------------------------------------------------------------------- /Resources/doc/index.rst: -------------------------------------------------------------------------------- 1 | Getting Started With MsalsasVotingBundle 2 | ======================================== 3 | 4 | This bundle provides a way for voting posts or whatever. 5 | 6 | Prerequisites 7 | ------------- 8 | 9 | This version of the bundle requires Symfony 4.0+. 10 | 11 | Translations 12 | ~~~~~~~~~~~~ 13 | 14 | If you wish to use default texts provided in this bundle, you have to make 15 | sure you have translator enabled in your config. 16 | 17 | .. code-block:: yaml 18 | 19 | # config/packages/translation.yaml 20 | 21 | framework: 22 | translator: ~ 23 | 24 | For more information about translations, check `Symfony documentation`_. 25 | 26 | .. _Symfony documentation: https://symfony.com/doc/current/book/translation.html 27 | 28 | 29 | Installation 30 | ------------ 31 | 32 | Installation is a quick 7 step process (4 if you use Flex): 33 | 34 | 1. Download MsalsasVotingBundle using composer 35 | 2. Enable the Bundle 36 | 3. Create your Reference class (post, article or whatever) 37 | 4. Configure the MsalsasVotingBundle 38 | 5. Create MsalsasVotingBundle routing 39 | 6. Update your database schema 40 | 7. Display the widgets 41 | 42 | 43 | Step 1: Download MsalsasVotingBundle using composer 44 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 | 46 | Require the bundle with composer: 47 | 48 | .. code-block:: bash 49 | 50 | $ composer require msalsas/voting-bundle 51 | 52 | Composer will install the bundle to your project's ``vendor/msalsas/voting-bundle`` directory. 53 | 54 | 55 | Step 2: Enable the bundle 56 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 57 | 58 | .. note:: 59 | 60 | If you're using Flex, this is done automatically 61 | 62 | Enable the bundle in the kernel: 63 | 64 | 166 | {{ msalsas_voting_widget.shakeIt(post.id) }} # Import the voting widget 167 |

168 | # This is just an example 169 | {{ post.title }} 170 | 171 |

172 | 173 | ... 174 | 175 | {{ msalsas_voting_widget.bottomBar(post.id) }} # Import bottom bar widget (includes negative voting form) 176 | 177 | 178 | Also, you have to import `Font Awesome`_ if you want to show the bottom bar icons 179 | 180 | 181 | Instead of using 182 | 183 | ``msalsas_voting_widget.shakeItCSS()`` 184 | 185 | ``msalsas_voting_widget.shakeItJS()`` 186 | 187 | you can import 188 | 189 | ``vendor/msalsas/voting-bundle/Resources/public/css/msalsas_voting_styles.css`` 190 | 191 | ``vendor/msalsas/voting-bundle/Resources/public/js/msalsas_voting_shakeIt.js`` 192 | 193 | with your assets. 194 | 195 | If you don't use ``shakeItModal`` errors will be displayed with ``alert``. 196 | 197 | .. _Font Awesome: https://fontawesome.com/how-to-use/on-the-web/setup/getting-started?using=web-fonts-with-css 198 | 199 | 200 | Next Steps 201 | ~~~~~~~~~~ 202 | 203 | Now that you have completed the basic installation and configuration of the 204 | MsalsasVotingBundle, you are ready to learn about more advanced features and usages 205 | of the bundle. 206 | 207 | The following documents are available: 208 | 209 | `1. Clicks or views`_. 210 | 211 | `2. Publishing`_. 212 | 213 | `3. Configuration reference`_. 214 | 215 | .. _1. Clicks or views: ./clicks_or_views.rst 216 | .. _2. Publishing: ./publish.rst 217 | .. _3. Configuration reference: ./configuration_reference.rst 218 | 219 | -------------------------------------------------------------------------------- /Resources/doc/publish.rst: -------------------------------------------------------------------------------- 1 | MsalsasVotingBundle publishing 2 | ============================== 3 | 4 | For displaying the published style for a reference you have to set it to published from a controller 5 | type-hinting the ``Voter`` and calling ``setPublished``: 6 | 7 | 8 | .. code-block:: php 9 | 10 | setPublished($post->getId()); 15 | 16 | // ... 17 | } 18 | 19 | 20 | You can also use ``isPublished`` method. 21 | 22 | Let's say you want to show all published posts in a page. You could use the repository to find them: 23 | 24 | .. code-block:: php 25 | 26 | em->getRepository(ReferenceVotes::class)->findBy(array('published' => true)); 29 | 30 | 31 | ``$referenceVotes`` will be an array of ``ReferenceVotes``, and you can get the reference id with: 32 | 33 | 34 | .. code-block:: php 35 | 36 | em->getRepository(ReferenceVotes::class)->findBy(array('published' => true)); 39 | foreach ($referenceVotes as $referenceVote) { 40 | $id = $referenceVote->getReference(); 41 | } -------------------------------------------------------------------------------- /Resources/doc/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msalsas/MsalsasVotingBundle/41a50b596f3e9e8fc2c829b035162e834efaa03c/Resources/doc/screenshot-1.png -------------------------------------------------------------------------------- /Resources/public/css/msalsas_voting_styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MsalsasVotingBundle package. 3 | * 4 | * (c) Manolo Salsas 5 | * 6 | * For the full copyright and license information, please view the LICENSE 7 | * file that was distributed with this source code. 8 | */ 9 | 10 | .msalsas-voting-widget-wrapper { 11 | display: inline-block; 12 | } 13 | .msalsas-voting-widget { 14 | font-size: 12px; 15 | color: #e35614; 16 | text-align: center; 17 | background-clip: padding-box; 18 | border-radius: 3px; 19 | font-family: 'Open Sans', sans-serif; 20 | width: 67px; 21 | } 22 | .msalsas-voting-shakes { 23 | background-color: #fff; 24 | padding: 6px 0 6px; 25 | border: 1px solid #FF883D; 26 | border-bottom: 0; 27 | line-height: 1.2em; 28 | background-repeat: repeat-x; 29 | background-position: 0 -2px; 30 | background-clip: padding-box; 31 | border-radius: 3px 3px 0 0; 32 | font-weight: bold; 33 | 34 | } 35 | .msalsas-voting-shakes.published { 36 | background-color: #FFE8D8; 37 | border: none; 38 | } 39 | .msalsas-voting-shakes a { 40 | display: block; 41 | color: #e35614; 42 | font-size: 18px; 43 | text-decoration: none; 44 | font-weight: bold; 45 | } 46 | .msalsas-voting-shake-it { 47 | overflow: hidden; 48 | display: block; 49 | height: 25px; 50 | position: relative; 51 | left: -6px; 52 | width: 78px; 53 | } 54 | .msalsas-voting-shake-it a, .msalsas-voting-shake-it span { 55 | background-color: #e35614; 56 | padding-top: 3px; 57 | display: block; 58 | box-sizing: border-box; 59 | height: 25px; 60 | color: #FFF; 61 | text-decoration: none; 62 | font-weight: bold; 63 | border-radius: 3px; 64 | } 65 | .msalsas-voting-shake-it a { 66 | background-image: linear-gradient(180deg, #F5720E 0%, #FE4A00 100%); 67 | box-shadow: 0 1px 2px 0 #C43E0B; 68 | border: 1px solid rgba(122, 47, 11, 0.45); 69 | } 70 | .msalsas-voting-shake-it span { 71 | background-image: none; 72 | border: 1px solid #ff6300; 73 | border-bottom: 1px solid #ff914a; 74 | } 75 | .msalsas-voting-clicks { 76 | background-color: #fff; 77 | padding: 1px 0 2px 0; 78 | border: 1px solid #FF883D; 79 | border-top: 0; 80 | font-size: 10px; 81 | background-repeat: repeat-x; 82 | background-position: bottom; 83 | background-clip: padding-box; 84 | border-radius: 0 0 3px 3px; 85 | font-weight: bold; 86 | } 87 | .msalsas-voting-clicks.published { 88 | background-color: #FFE8D8; 89 | border: none; 90 | } 91 | .msalsas-voting-bottom-bar-wrapper { 92 | font-family: 'Open Sans', sans-serif; 93 | float: right; 94 | margin-right: 50px; 95 | display: inline-block; 96 | padding: 0; 97 | color: #3c3c3c; 98 | border-top: 1px solid #FEE7DB; 99 | border-bottom: 1px solid #FEE7DB; 100 | height: 32px; 101 | } 102 | .msalsas-voting-bottom-bar-wrapper span { 103 | padding-right: 5px; 104 | font-size: 11px; 105 | font-weight: bold; 106 | line-height: 30px; 107 | } 108 | .msalsas-voting-bottom-bar-wrapper span i { 109 | color: #ffa980; 110 | } 111 | .msalsas-voting-bottom-bar-votes-up { 112 | margin-left: 10px; 113 | } 114 | .msalsas-voting-bottom-bar form { 115 | display: inline-block; 116 | margin-left: 10px; 117 | padding: 0; 118 | border-left: 1px solid #D8D8D8; 119 | } 120 | .msalsas-voting-bottom-bar form select { 121 | background: #FFF; 122 | color: #e35614; 123 | border: 0; 124 | font-size: 11px; 125 | width: 75px; 126 | border-radius: 0; 127 | } 128 | 129 | /*Modal*/ 130 | #msalsas-modal { 131 | display: none; /* Hidden by default */ 132 | position: fixed; /* Stay in place */ 133 | z-index: 1; /* Sit on top */ 134 | left: 0; 135 | top: 0; 136 | width: 100%; 137 | height: 100%; 138 | overflow: auto; 139 | background-color: rgb(0,0,0); 140 | background-color: rgba(0,0,0,0.4); 141 | } 142 | 143 | /* Modal Content/Box */ 144 | .msalsas-modal-content { 145 | background-color: #FFEEC7; 146 | margin: 15% auto; 147 | padding: 20px; 148 | border: 1px solid #e35614; 149 | min-width: 250px; 150 | text-align: center; 151 | width: 25%; 152 | height: 18.5ex; 153 | overflow: hidden; 154 | border-radius: 3px; 155 | } 156 | #msalsas-modal-text { 157 | font-weight: bold; 158 | font-size: 20px; 159 | margin-top: 30px; 160 | text-align: center; 161 | } 162 | 163 | .msalsas-close { 164 | color: #aaa; 165 | float: right; 166 | font-size: 28px; 167 | font-weight: bold; 168 | line-height: 15px; 169 | } 170 | 171 | .msalsas-close:hover, 172 | .msalsas-close:focus { 173 | color: black; 174 | text-decoration: none; 175 | cursor: pointer; 176 | } -------------------------------------------------------------------------------- /Resources/public/js/msalsas_voting_bottomBar.js: -------------------------------------------------------------------------------- 1 | // This file is part of the MsalsasVotingBundle package. 2 | // 3 | // (c) Manolo Salsas 4 | // 5 | // For the full copyright and license information, please view the LICENSE 6 | // file that was distributed with this source code. 7 | 8 | (function() { 9 | document.addEventListener('DOMContentLoaded', function() { 10 | var voteNegativeForm = document.querySelectorAll('.msalsas-voting-bottom-bar form'); 11 | for (var i = 0; i < voteNegativeForm.length; i++) { 12 | if (voteNegativeForm[i].addEventListener) { 13 | voteNegativeForm[i].addEventListener('change', voteNegative, false); 14 | } else { 15 | voteNegativeForm[i].attachEvent('onchange', voteNegative); 16 | } 17 | } 18 | }); 19 | 20 | function voteNegative(evt) { 21 | var form = evt.target.parentNode; 22 | var id = form.dataset.id; 23 | var options = form.ratings.options; 24 | window.msalsasVoting_Selected = options[form.ratings.selectedIndex]; 25 | var elem = document.getElementById('msalsas-voting-problem-' + id); 26 | var url = elem.dataset.url; 27 | var http = new XMLHttpRequest(); 28 | http.open('POST', url, true); 29 | http.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 30 | 31 | http.onreadystatechange = function() { 32 | if(http.readyState == 4 && http.status == 200) { 33 | var downVotes = document.getElementById('msalsas-voting-bottom-bar-votes-down-' + id); 34 | downVotes.innerHTML = document.createTextNode(http.responseText).wholeText; 35 | var buttonElem = document.getElementById('msalsas-voting-a-shake-' + id); 36 | buttonElem.innerHTML = '' + window.msalsasVoting_Selected.text + ''; 37 | } else if(http.readyState == 4 && http.status >= 400) { 38 | if (http.responseText.length < 50) { 39 | showModal(http.responseText); 40 | } else { 41 | showModal('Error'); 42 | } 43 | } 44 | }; 45 | if (window.msalsasVoting_Selected.value !== '0') { 46 | http.send(window.msalsasVoting_Selected.value); 47 | } 48 | } 49 | 50 | function showModal(message) { 51 | message = message.replace (/(^")|("$)/g, ''); 52 | var modal = document.getElementById('msalsas-modal'); 53 | var span = document.getElementsByClassName("msalsas-close")[0]; 54 | 55 | if (!modal || !span) { 56 | alert(message); 57 | return; 58 | } 59 | document.getElementById('msalsas-modal-text').innerText = message; 60 | modal.style.display = "block"; 61 | 62 | if (span.addEventListener) { 63 | span.addEventListener('click', closeModal, false); 64 | } else { 65 | span.attachEvent('onclick', closeModal); 66 | } 67 | 68 | if (window.addEventListener) { 69 | window.addEventListener('click', closeModal, false); 70 | } else { 71 | window.attachEvent('onclick', closeModal); 72 | } 73 | } 74 | 75 | function closeModal(event) { 76 | var modal = document.getElementById('msalsas-modal'); 77 | var span = document.getElementsByClassName("msalsas-close")[0]; 78 | if (event.target === modal || event.target === span) { 79 | modal.style.display = "none"; 80 | } 81 | } 82 | })(); -------------------------------------------------------------------------------- /Resources/public/js/msalsas_voting_shakeIt.js: -------------------------------------------------------------------------------- 1 | // This file is part of the MsalsasVotingBundle package. 2 | // 3 | // (c) Manolo Salsas 4 | // 5 | // For the full copyright and license information, please view the LICENSE 6 | // file that was distributed with this source code. 7 | 8 | (function() { 9 | document.addEventListener('DOMContentLoaded', function() { 10 | var shakeItLink = document.querySelectorAll('.msalsas-voting-shake-it a'); 11 | for (var i = 0; i < shakeItLink.length; i++) { 12 | if (shakeItLink[i].addEventListener) { 13 | shakeItLink[i].addEventListener('click', shakeIt, false); 14 | } else { 15 | shakeItLink[i].attachEvent('onclick', shakeIt); 16 | } 17 | } 18 | }); 19 | 20 | function shakeIt(evt) { 21 | var shakeItButton = evt.target.parentNode; 22 | var id = shakeItButton.dataset.id; 23 | var url = shakeItButton.dataset.url; 24 | var shakenText = shakeItButton.dataset.shakentext; 25 | var http = new XMLHttpRequest(); 26 | http.open('POST', url, true); 27 | http.setRequestHeader('Content-type', 'application/json'); 28 | http.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 29 | 30 | http.onreadystatechange = function() { 31 | if(http.readyState == 4 && http.status == 200) { 32 | var shakesElem = document.getElementById('msalsas-voting-shakes-' + id); 33 | shakesElem.text = document.createTextNode(http.responseText).wholeText; 34 | var buttonElem = document.getElementById('msalsas-voting-a-shake-' + id); 35 | buttonElem.innerHTML = '' + shakenText + ''; 36 | } else if(http.readyState == 4 && http.status >= 400) { 37 | showModal(http.responseText); 38 | } 39 | }; 40 | http.send(); 41 | } 42 | 43 | function showModal(message) { 44 | message = message.replace (/(^")|("$)/g, ''); 45 | var modal = document.getElementById('msalsas-modal'); 46 | var span = document.getElementsByClassName("msalsas-close")[0]; 47 | 48 | if (!modal || !span) { 49 | alert(message); 50 | return; 51 | } 52 | document.getElementById('msalsas-modal-text').innerText = message; 53 | modal.style.display = "block"; 54 | 55 | if (span.addEventListener) { 56 | span.addEventListener('click', closeModal, false); 57 | } else { 58 | span.attachEvent('onclick', closeModal); 59 | } 60 | 61 | if (window.addEventListener) { 62 | window.addEventListener('click', closeModal, false); 63 | } else { 64 | window.attachEvent('onclick', closeModal); 65 | } 66 | } 67 | 68 | function closeModal(event) { 69 | var modal = document.getElementById('msalsas-modal'); 70 | var span = document.getElementsByClassName("msalsas-close")[0]; 71 | if (event.target === modal || event.target === span) { 72 | modal.style.display = "none"; 73 | } 74 | } 75 | })(); -------------------------------------------------------------------------------- /Resources/translations/messages.en.yml: -------------------------------------------------------------------------------- 1 | msalsas_voting: 2 | votes: shakes 3 | shake_it: shake it 4 | shaken: shaken 5 | clicks: "{0} %count% clicks|{1} %count% click|]1,Inf[ %count% clicks" 6 | problem: problem 7 | positive_votes: Positive votes 8 | anonymous_votes: Anonymous votes 9 | negative_votes: Negative votes 10 | negative_reasons: 11 | irrelevant: irrelevant 12 | old: old 13 | tiredness: tiredness 14 | sensationalist: sensationalist 15 | spam: spam 16 | duplicated: duplicated 17 | microblogging: microblogging 18 | erroneous: erroneous 19 | plagiarism: copy/plagiarism 20 | errors: 21 | anon_cannot_vote_negative: 'Anonymous user cannot vote negative.' 22 | ref_does_not_exist: 'Reference %reference% does not exist.' 23 | no_ip_defined_for_anon: 'No IP has been defined for anonymous user.' 24 | already_voted: 'You have already voted.' 25 | already_clicked: 'You have already clicked.' 26 | too_much_anon_votes: 'Too much anonymous votes.' 27 | invalid_reason: 'Invalid reason.' 28 | -------------------------------------------------------------------------------- /Resources/translations/messages.es.yml: -------------------------------------------------------------------------------- 1 | msalsas_voting: 2 | votes: meneos 3 | shake_it: menéalo 4 | shaken: ¡hecho! 5 | clicks: "{0} %count% clics|{1} %count% clic|]1,Inf[ %count% clics" 6 | problem: problema 7 | positive_votes: Votos positivos 8 | anonymous_votes: Votos anónimos 9 | negative_votes: Votos negativos 10 | negative_reasons: 11 | irrelevant: irrelevante 12 | old: antigua 13 | tiredness: cansina 14 | sensationalist: sensacionalista 15 | spam: spam 16 | duplicated: duplicada 17 | microblogging: microblogging 18 | erroneous: errónea 19 | plagiarism: copia/plagio 20 | errors: 21 | anon_cannot_vote_negative: 'Los usuarios anonimos no pueden votar negativo.' 22 | ref_does_not_exist: 'La referencia %reference% no existe.' 23 | no_ip_defined_for_anon: 'No ha sido definida ninguna IP para el usuario anonimo.' 24 | already_voted: 'Ya has votado.' 25 | already_clicked: 'Ya has hecho clic.' 26 | too_much_anon_votes: 'Demasiados votos anonimos.' 27 | invalid_reason: 'Razon invalida.' -------------------------------------------------------------------------------- /Resources/views/msalsas_voting_widget.html.twig: -------------------------------------------------------------------------------- 1 | {# This file is part of the MsalsasVotingBundle package. 2 | 3 | (c) Manolo Salsas 4 | 5 | For the full copyright and license information, please view the LICENSE 6 | file that was distributed with this source code. #} 7 | 8 | {% macro shakeIt(id) %} 9 | {% set votes = msalsas_voting_voter.positiveVotes(id) %} 10 | {% set userVote = msalsas_voting_voter.userVote(id) %} 11 | {% set clicks = msalsas_voting_clicker.clicks(id) %} 12 | {% set published = msalsas_voting_voter.published(id) %} 13 |
14 |
15 |
16 | {{ votes|default(0) }} {{ 'msalsas_voting.votes'|trans }} 17 |
18 |
23 | {% if not userVote %} 24 | {{ 'msalsas_voting.shake_it'|trans }} 25 | {% elseif userVote.positive %} 26 | {{ 'msalsas_voting.shaken'|trans }} 27 | {% else %} 28 | {{ userVote.reason|trans }} 29 | {% endif %} 30 |
31 |
32 | {% if clicks %} 33 | {{ 'msalsas_voting.clicks'|transchoice(clicks) }} 34 | {% else %} 35 |   36 | {% endif %} 37 |
38 |
39 |
40 | {% endmacro shakeIt %} 41 | 42 | {% macro bottomBar(id) %} 43 | {% set votes = msalsas_voting_voter.positiveVotes(id) %} 44 | {% set anonymousVotes = msalsas_voting_voter.anonymousVotes(id) %} 45 | {% set userPositiveVotes = msalsas_voting_voter.userPositiveVotes(id) %} 46 | {% set negativeVotes = msalsas_voting_voter.negativeVotes(id) %} 47 | {% set userVote = msalsas_voting_voter.userVote(id) %} 48 | {% set negativeReasons = msalsas_voting_voter.negativeReasons() %} 49 |
50 |
51 | {{ userPositiveVotes|default(0) }} 52 | {{ anonymousVotes|default(0) }} 53 | {{ negativeVotes|default(0) }} 54 | 55 | {% if not userVote and msalsas_voting_voter.userCanVoteNegative(id) %} 56 |
61 | 67 |
68 | {% endif %} 69 |
70 |
71 | {% endmacro bottomBar %} 72 | 73 | {% macro shakeItModal() %} 74 |
75 |
76 | × 77 |

Error

78 |
79 |
80 | {% endmacro %} 81 | 82 | {% macro shakeItCSS() %} 83 | 84 | {% endmacro shakeItCSS %} 85 | 86 | {% macro shakeItJS() %} 87 | 88 | 89 | {% endmacro shakeItJS %} 90 | -------------------------------------------------------------------------------- /Service/Clicker.php: -------------------------------------------------------------------------------- 1 | em = $em; 50 | $this->token = $token; 51 | $this->request = $requestStack->getCurrentRequest(); 52 | $this->translator = $translator; 53 | } 54 | 55 | public function getClicks($referenceId) 56 | { 57 | /** @var ReferenceClicks|null $referenceClicks */ 58 | $referenceClicks = $this->em->getRepository(ReferenceClicks::class)->findOneBy(array('reference' => $referenceId)); 59 | if (!$referenceClicks) { 60 | return 0; 61 | } 62 | 63 | return $referenceClicks->getClicks(); 64 | } 65 | 66 | public function addClick($referenceId) 67 | { 68 | $user = $this->token->getToken()->getUser(); 69 | $user = $user instanceof UserInterface ? $user : null; 70 | 71 | try { 72 | $this->validateClick($user, $referenceId); 73 | } catch (\Exception $e) { 74 | return $this->getClicks($referenceId); 75 | } 76 | 77 | $click = new Click(); 78 | $click->setReference($referenceId); 79 | $click->setUser($user); 80 | $click->setUserIP($this->request->getClientIp()); 81 | 82 | /** @var \Msalsas\VotingBundle\Entity\ReferenceClicks $referenceClicks */ 83 | $referenceClicks = $this->addReferenceClick($referenceId); 84 | 85 | $this->em->persist($click); 86 | $this->em->persist($referenceClicks); 87 | $this->em->flush(); 88 | 89 | return $referenceClicks->getClicks(); 90 | } 91 | 92 | private function validateClick($user, $referenceId) 93 | { 94 | if (!$user instanceof UserInterface && !$this->request->getClientIp()) { 95 | throw new AccessDeniedException($this->translator->trans('msalsas_voting.errors.no_ip_defined_for_anon')); 96 | } 97 | 98 | if ($this->userHasClicked($user, $referenceId)) { 99 | throw new AccessDeniedException($this->translator->trans('msalsas_voting.errors.already_clicked')); 100 | } 101 | } 102 | 103 | private function userHasClicked($user, $referenceId) 104 | { 105 | $clickRepository = $this->em->getRepository(Click::class); 106 | 107 | if ($user instanceof UserInterface) { 108 | if ($clickRepository->findOneBy( 109 | array( 110 | 'user' => $user, 111 | 'reference' => $referenceId 112 | ) 113 | )) { 114 | return true; 115 | } 116 | } else { 117 | if ($clickRepository->findOneBy( 118 | array( 119 | 'user' => null, 120 | 'userIP' => $this->request->getClientIp(), 121 | 'reference' => $referenceId 122 | ) 123 | )) { 124 | return true; 125 | } 126 | } 127 | 128 | return false; 129 | } 130 | 131 | private function addReferenceClick($referenceId) 132 | { 133 | /** @var ReferenceClicks|null $referenceClicks */ 134 | $referenceClicks = $this->em->getRepository(ReferenceClicks::class)->findOneBy(array('reference' => $referenceId)); 135 | if ($referenceClicks) { 136 | $referenceClicks->addClick(); 137 | } else { 138 | $referenceClicks = new ReferenceClicks(); 139 | $referenceClicks->setReference($referenceId); 140 | $referenceClicks->addClick(); 141 | } 142 | 143 | return $referenceClicks; 144 | } 145 | } -------------------------------------------------------------------------------- /Service/Voter.php: -------------------------------------------------------------------------------- 1 | em = $em; 66 | $this->token = $token; 67 | $this->request = $requestStack->getCurrentRequest(); 68 | $this->translator = $translator; 69 | $this->anonPercentAllowed = $anonPercentAllowed; 70 | $this->anonMinAllowed = $anonMinAllowed; 71 | $this->negativeReasons = $negativeReasons; 72 | } 73 | 74 | public function getPositiveVotes($referenceId) 75 | { 76 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes|null $referenceVotes */ 77 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 78 | if (!$referenceVotes) { 79 | $referenceVotes = new ReferenceVotes(); 80 | $referenceVotes->setReference($referenceId); 81 | } 82 | 83 | return $referenceVotes->getPositiveVotes(); 84 | } 85 | 86 | public function getNegativeVotes($referenceId) 87 | { 88 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes|null $referenceVotes */ 89 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 90 | if (!$referenceVotes) { 91 | $referenceVotes = new ReferenceVotes(); 92 | $referenceVotes->setReference($referenceId); 93 | } 94 | 95 | return $referenceVotes->getNegativeVotes(); 96 | } 97 | 98 | public function getUserPositiveVotes($referenceId) 99 | { 100 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes|null $referenceVotes */ 101 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 102 | if (!$referenceVotes) { 103 | $referenceVotes = new ReferenceVotes(); 104 | $referenceVotes->setReference($referenceId); 105 | } 106 | 107 | return $referenceVotes->getUserVotes(); 108 | } 109 | 110 | public function getAnonymousVotes($referenceId) 111 | { 112 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes|null $referenceVotes */ 113 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 114 | if (!$referenceVotes) { 115 | $referenceVotes = new ReferenceVotes(); 116 | $referenceVotes->setReference($referenceId); 117 | } 118 | 119 | return $referenceVotes->getAnonymousVotes(); 120 | } 121 | 122 | public function votePositive($referenceId) 123 | { 124 | $user = $this->token->getToken() ? $this->token->getToken()->getUser() : null; 125 | 126 | $this->validateVote($user, $referenceId); 127 | 128 | $user = $user instanceof UserInterface ? $user : null; 129 | 130 | $vote = new VotePositive(); 131 | $vote->setReference($referenceId); 132 | $vote->setUser($user); 133 | $vote->setUserIP($this->request ? $this->request->getClientIp() : null); 134 | 135 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes $referenceVotes */ 136 | $referenceVotes = $this->addReferenceVote($referenceId, true, !$user); 137 | 138 | $this->em->persist($vote); 139 | $this->em->persist($referenceVotes); 140 | $this->em->flush(); 141 | 142 | return $referenceVotes->getPositiveVotes(); 143 | } 144 | 145 | public function voteNegative($referenceId, $reason) 146 | { 147 | $user = $this->token->getToken() ? $this->token->getToken()->getUser() : null; 148 | 149 | if (!$reason || !is_string($reason) || $reason === '0') { 150 | throw new AccessDeniedException($this->translator->trans('msalsas_voting.errors.invalid_reason')); 151 | } 152 | 153 | $this->validateVote($user, $referenceId); 154 | 155 | if (!$user instanceof UserInterface) { 156 | throw new AccessDeniedException($this->translator->trans('msalsas_voting.errors.anon_cannot_vote_negative')); 157 | } 158 | 159 | $vote = new VoteNegative(); 160 | $vote->setReference($referenceId); 161 | $vote->setReason($reason); 162 | $vote->setUser($user); 163 | $vote->setUserIP($this->request->getClientIp()); 164 | 165 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes $referenceVotes */ 166 | $referenceVotes = $this->addReferenceVote($referenceId, false, !$user); 167 | 168 | $this->em->persist($referenceVotes); 169 | $this->em->persist($vote); 170 | $this->em->flush(); 171 | 172 | return $referenceVotes->getNegativeVotes(); 173 | } 174 | 175 | public function getUserVote($referenceId) 176 | { 177 | $user = $this->token->getToken()->getUser(); 178 | $user = $user instanceof UserInterface ? $user : null; 179 | $votePositiveRepository = $this->em->getRepository(VotePositive::class); 180 | $voteNegativeRepository = $this->em->getRepository(VoteNegative::class); 181 | 182 | if ($user && $vote = $votePositiveRepository->findOneBy(array('user' => $user, 'reference' => $referenceId))) { 183 | return $vote; 184 | } else if (!$user && $vote = $votePositiveRepository->findOneBy(array('user' => $user, 'reference' => $referenceId, 'userIP' => $this->request->getClientIp()))) { 185 | return $vote; 186 | } else if ($vote = $voteNegativeRepository->findOneBy(array('user' => $user, 'reference' => $referenceId))) { 187 | return $vote; 188 | } 189 | 190 | return false; 191 | } 192 | 193 | public function getNegativeReasons() 194 | { 195 | return $this->negativeReasons; 196 | } 197 | 198 | public function userCanVoteNegative($referenceId) 199 | { 200 | $user = $this->token->getToken()->getUser(); 201 | if (!$user instanceof UserInterface) { 202 | return false; 203 | } 204 | 205 | return !$this->userHasVoted($user, $referenceId); 206 | } 207 | 208 | public function isPublished($referenceId) 209 | { 210 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes|null $referenceVotes */ 211 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 212 | if (!$referenceVotes) { 213 | return false; 214 | } 215 | 216 | return $referenceVotes->isPublished(); 217 | } 218 | 219 | public function setPublished($referenceId) 220 | { 221 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes|null $referenceVotes */ 222 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 223 | if (!$referenceVotes) { 224 | throw new \Exception($this->translator->trans('msalsas_voting.errors.ref_does_not_exist', array('%reference%' => $referenceId))); 225 | } 226 | 227 | $referenceVotes->setPublished(true); 228 | } 229 | 230 | protected function validateVote($user, $referenceId) 231 | { 232 | if (!$user instanceof UserInterface && (!$this->request || !$this->request->getClientIp())) { 233 | throw new AccessDeniedException($this->translator->trans('msalsas_voting.errors.no_ip_defined_for_anon')); 234 | } 235 | 236 | if ($this->userHasVoted($user, $referenceId)) { 237 | throw new AccessDeniedException($this->translator->trans('msalsas_voting.errors.already_voted')); 238 | } 239 | 240 | if (!$user instanceof UserInterface) { 241 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 242 | if ($referenceVotes instanceof ReferenceVotes && !$this->anonymousIsAllowed($referenceVotes)) { 243 | throw new AccessDeniedException($this->translator->trans('msalsas_voting.errors.too_much_anon_votes')); 244 | } 245 | } 246 | } 247 | 248 | protected function userHasVoted($user, $referenceId) 249 | { 250 | $votePositiveRepository = $this->em->getRepository(VotePositive::class); 251 | $voteNegativeRepository = $this->em->getRepository(VoteNegative::class); 252 | 253 | if ($user instanceof UserInterface) { 254 | if ($votePositiveRepository->findOneBy( 255 | array( 256 | 'user' => $user, 257 | 'reference' => $referenceId 258 | ) 259 | )) { 260 | return true; 261 | } else if ($voteNegativeRepository->findOneBy( 262 | array( 263 | 'user' => $user, 264 | 'reference' => $referenceId 265 | ) 266 | )) { 267 | return true; 268 | } 269 | } else { 270 | if ($votePositiveRepository->findOneBy( 271 | array( 272 | 'user' => null, 273 | 'userIP' => $this->request->getClientIp(), 274 | 'reference' => $referenceId 275 | ) 276 | )) { 277 | return true; 278 | } 279 | } 280 | 281 | return false; 282 | } 283 | 284 | protected function addReferenceVote($referenceId, $positive, $anonymous = true) 285 | { 286 | /** @var \Msalsas\VotingBundle\Entity\ReferenceVotes|null $referenceVotes */ 287 | $referenceVotes = $this->em->getRepository(ReferenceVotes::class)->findOneBy(array('reference' => $referenceId)); 288 | if ($referenceVotes) { 289 | $referenceVotes->addVote($positive, $anonymous); 290 | } else { 291 | $referenceVotes = new ReferenceVotes(); 292 | $referenceVotes->setReference($referenceId); 293 | $referenceVotes->addVote($positive, $anonymous); 294 | } 295 | 296 | return $referenceVotes; 297 | } 298 | 299 | protected function anonymousIsAllowed(ReferenceVotes $referenceVotes) 300 | { 301 | $anonVotes = $referenceVotes->getAnonymousVotes(); 302 | $userVotes = $referenceVotes->getUserVotes(); 303 | 304 | if ($anonVotes < $this->anonMinAllowed) { 305 | return true; 306 | } 307 | 308 | $anonPercent = $anonVotes ? ($anonVotes / ($userVotes + $anonVotes)) * 100 : 0; 309 | if ($anonPercent < $this->anonPercentAllowed) { 310 | return true; 311 | } 312 | 313 | return false; 314 | } 315 | } -------------------------------------------------------------------------------- /Tests/DependencyInjection/MsalsasVotingExtensionTest.php: -------------------------------------------------------------------------------- 1 | configuration = null; 27 | } 28 | 29 | /** 30 | * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException 31 | */ 32 | public function testUserLoadThrowsExceptionUnlessUserProviderSet() 33 | { 34 | $this->configuration = new ContainerBuilder(); 35 | $loader = new MsalsasVotingExtension(); 36 | $config = $this->getEmptyConfig(); 37 | unset($config['user_provider']); 38 | $loader->load(array($config), $this->configuration); 39 | } 40 | 41 | /** 42 | * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException 43 | */ 44 | public function testUserLoadThrowsExceptionUnlessUserProviderIsValid() 45 | { 46 | $this->configuration = new ContainerBuilder(); 47 | $loader = new MsalsasVotingExtension(); 48 | $config = $this->getEmptyConfig(); 49 | $config['user_provider'] = 5; 50 | $loader->load(array($config), $this->configuration); 51 | } 52 | 53 | public function testUserProvider() 54 | { 55 | $this->configuration = new ContainerBuilder(); 56 | $loader = new MsalsasVotingExtension(); 57 | $config = $this->getFullConfig(); 58 | $config['user_provider'] = '\App\Entity\User'; 59 | $loader->load(array($config), $this->configuration); 60 | 61 | $this->assertHasDefinition('msalsas_voting.voter'); 62 | $this->assertHasDefinition('msalsas_voting.clicker'); 63 | $this->assertParameter(50, 'msalsas_voting.anonymous_percent_allowed'); 64 | $this->assertParameter(2, 'msalsas_voting.anonymous_min_allowed'); 65 | } 66 | 67 | public function testPrependConfig() 68 | { 69 | $this->configuration = new ContainerBuilder(); 70 | $loader = new MsalsasVotingExtension(); 71 | $config = $this->getFullConfig(); 72 | $config['user_provider'] = '\App\Entity\User'; 73 | 74 | $this->configuration->prependExtensionConfig('msalsas_voting', $config); 75 | $loader->prepend($this->configuration); 76 | 77 | // Doctrine config 78 | $doctrineConfig = $this->configuration->getExtensionConfig('doctrine'); 79 | $this->assertTrue(isset($doctrineConfig) && is_array($doctrineConfig)); 80 | $this->assertTrue(isset($doctrineConfig[0]['orm']['resolve_target_entities']['Msalsas\VotingBundle\Entity\Vote\UserInterface'])); 81 | $this->assertTrue($doctrineConfig[0]['orm']['resolve_target_entities']['Msalsas\VotingBundle\Entity\Vote\UserInterface'] === '\App\Entity\User'); 82 | $this->assertTrue(isset($doctrineConfig[0]['orm']['mappings'])); 83 | $mapping = array( 84 | 'name' => 'MsalsasVotingBundle', 85 | 'is_bundle' => true, 86 | 'type' => 'xml', 87 | 'prefix' => 'Msalsas\VotingBundle\Entity' 88 | ); 89 | $this->assertTrue(in_array($mapping, $doctrineConfig[0]['orm']['mappings'])); 90 | 91 | // Twig config 92 | $twigConfig = $this->configuration->getExtensionConfig('twig'); 93 | $this->assertTrue(isset($twigConfig) && is_array($twigConfig)); 94 | $this->assertTrue(isset($twigConfig[0]['globals']['msalsas_voting_voter'])); 95 | $this->assertTrue($twigConfig[0]['globals']['msalsas_voting_voter'] === "@msalsas_voting.voter"); 96 | $this->assertTrue(isset($twigConfig[0]['globals']['msalsas_voting_clicker'])); 97 | $this->assertTrue($twigConfig[0]['globals']['msalsas_voting_clicker'] === "@msalsas_voting.clicker"); 98 | 99 | $mainDir = substr(__DIR__, 0, strpos(__DIR__, '/Tests/DependencyInjection') ); 100 | $this->assertTrue(isset($twigConfig[0]['paths'][$mainDir . '/DependencyInjection/../Resources/views'])); 101 | $this->assertTrue($twigConfig[0]['paths'][$mainDir . '/DependencyInjection/../Resources/views'] === "msalsas_voting"); 102 | $this->assertTrue(isset($twigConfig[0]['paths'][$mainDir . '/DependencyInjection/../Resources/public'])); 103 | $this->assertTrue($twigConfig[0]['paths'][$mainDir . '/DependencyInjection/../Resources/public'] === "msalsas_voting.public"); 104 | } 105 | 106 | /** 107 | * getEmptyConfig. 108 | * 109 | * @return array 110 | */ 111 | protected function getEmptyConfig() 112 | { 113 | $yaml = <<parse($yaml); 119 | } 120 | 121 | /** 122 | * getFullConfig. 123 | * 124 | * @return array 125 | */ 126 | protected function getFullConfig() 127 | { 128 | $yaml = <<parse($yaml); 139 | } 140 | 141 | /** 142 | * @param mixed $value 143 | * @param string $key 144 | */ 145 | private function assertParameter($value, $key) 146 | { 147 | $this->assertSame($value, $this->configuration->getParameter($key), sprintf('%s parameter is correct', $key)); 148 | } 149 | 150 | /** 151 | * @param string $id 152 | */ 153 | private function assertHasDefinition($id) 154 | { 155 | $this->assertTrue(($this->configuration->hasDefinition($id) ?: $this->configuration->hasAlias($id))); 156 | } 157 | } -------------------------------------------------------------------------------- /Tests/Entity/ClickTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(UserMock::class) 14 | ->getMock(); 15 | $click = new Click(); 16 | $click->setId(1); 17 | $click->setReference(1); 18 | $click->setUser($userMock); 19 | $click->setUserIP('127.0.0.1'); 20 | 21 | $this->assertSame(1, $click->getId()); 22 | $this->assertSame(1, $click->getReference()); 23 | $this->assertSame($userMock, $click->getUser()); 24 | $this->assertSame('127.0.0.1', $click->getUserIP()); 25 | } 26 | } -------------------------------------------------------------------------------- /Tests/Entity/ReferenceClicksTest.php: -------------------------------------------------------------------------------- 1 | setReference(1); 14 | $referenceClicks->setClicks(100); 15 | 16 | $this->assertSame(1, $referenceClicks->getReference()); 17 | $this->assertSame(100, $referenceClicks->getClicks()); 18 | } 19 | } -------------------------------------------------------------------------------- /Tests/Entity/ReferenceVotesTest.php: -------------------------------------------------------------------------------- 1 | setReference(1); 14 | $referenceVotes->setPositiveVotes(880); 15 | $referenceVotes->setAnonymousVotes(80); 16 | $referenceVotes->setUserVotes(800); 17 | $referenceVotes->setNegativeVotes(8); 18 | $referenceVotes->setPublished(true); 19 | 20 | $this->assertSame(1, $referenceVotes->getReference()); 21 | $this->assertSame(880, $referenceVotes->getPositiveVotes()); 22 | $this->assertSame(80, $referenceVotes->getAnonymousVotes()); 23 | $this->assertSame(800, $referenceVotes->getUserVotes()); 24 | $this->assertSame(8, $referenceVotes->getNegativeVotes()); 25 | $this->assertSame(true, $referenceVotes->isPublished()); 26 | } 27 | } -------------------------------------------------------------------------------- /Tests/Entity/VoteNegativeTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(UserMock::class) 14 | ->getMock(); 15 | $votePositive = new VoteNegative(); 16 | $votePositive->setId(1); 17 | $votePositive->setReference(1); 18 | $votePositive->setUser($userMock); 19 | $votePositive->setUserIP('127.0.0.1'); 20 | $votePositive->setReason('irrelevant'); 21 | 22 | $this->assertSame(1, $votePositive->getId()); 23 | $this->assertSame(1, $votePositive->getReference()); 24 | $this->assertSame($userMock, $votePositive->getUser()); 25 | $this->assertSame('127.0.0.1', $votePositive->getUserIP()); 26 | $this->assertSame('irrelevant', $votePositive->getReason()); 27 | $this->assertSame(false, $votePositive->isPositive()); 28 | } 29 | } -------------------------------------------------------------------------------- /Tests/Entity/VotePositiveTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(UserMock::class) 14 | ->getMock(); 15 | $votePositive = new VotePositive(); 16 | $votePositive->setId(1); 17 | $votePositive->setReference(1); 18 | $votePositive->setUser($userMock); 19 | $votePositive->setUserIP('127.0.0.1'); 20 | 21 | $this->assertSame(1, $votePositive->getId()); 22 | $this->assertSame(1, $votePositive->getReference()); 23 | $this->assertSame($userMock, $votePositive->getUser()); 24 | $this->assertSame('127.0.0.1', $votePositive->getUserIP()); 25 | $this->assertSame(true, $votePositive->isPositive()); 26 | } 27 | } -------------------------------------------------------------------------------- /Tests/Mock/AnonymousUserMock.php: -------------------------------------------------------------------------------- 1 | user = $user; 23 | $this->userIP = $userIP; 24 | } 25 | 26 | public function getId() 27 | { 28 | return 1; 29 | } 30 | 31 | public function getUser() 32 | { 33 | return $this->user; 34 | } 35 | 36 | public function getReference() 37 | { 38 | return 1; 39 | } 40 | 41 | public function getUserIP() 42 | { 43 | return $this->userIP; 44 | } 45 | } -------------------------------------------------------------------------------- /Tests/Mock/UserMock.php: -------------------------------------------------------------------------------- 1 | user = $user; 24 | } 25 | 26 | public function getId() 27 | { 28 | return 1; 29 | } 30 | 31 | public function getUser() 32 | { 33 | return $this->user; 34 | } 35 | 36 | public function getReference() 37 | { 38 | return 1; 39 | } 40 | 41 | public function getUserIP() 42 | { 43 | return $this->userIP; 44 | } 45 | 46 | public function isPositive() 47 | { 48 | return true; 49 | } 50 | 51 | public function getReason() 52 | { 53 | return $this->reason; 54 | } 55 | } -------------------------------------------------------------------------------- /Tests/Mock/VotePositiveMock.php: -------------------------------------------------------------------------------- 1 | user = $user; 23 | } 24 | 25 | public function getId() 26 | { 27 | return 1; 28 | } 29 | 30 | public function getUser() 31 | { 32 | return $this->user; 33 | } 34 | 35 | public function getReference() 36 | { 37 | return 1; 38 | } 39 | 40 | public function getUserIP() 41 | { 42 | return $this->userIP; 43 | } 44 | 45 | public function isPositive() 46 | { 47 | return true; 48 | } 49 | } -------------------------------------------------------------------------------- /Tests/Service/AbstractServiceTest.php: -------------------------------------------------------------------------------- 1 | setDefaultMocks(); 38 | $this->translator = new Translator('en'); 39 | } 40 | 41 | protected function setDefaultMocks() 42 | { 43 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 44 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 45 | $this->emMock->method('getRepository')->willReturn($emRepositoryMock); 46 | 47 | $requestMock = $this->getMockBuilder(Request::class) 48 | ->disableOriginalConstructor() 49 | ->setMethods(array('getClientIp')) 50 | ->getMock(); 51 | $requestMock->expects($this->any()) 52 | ->method('getClientIp')->willReturn('127.0.0.1'); 53 | 54 | $this->requestStackMock = $this->getMockBuilder(RequestStack::class) 55 | ->disableOriginalConstructor() 56 | ->setMethods(array('getCurrentRequest')) 57 | ->getMock(); 58 | $this->requestStackMock->expects($this->any()) 59 | ->method('getCurrentRequest')->willReturn($requestMock); 60 | } 61 | 62 | protected function setUserMocks() 63 | { 64 | $this->userMock = $this->getMockBuilder(UserMock::class) 65 | ->getMock(); 66 | 67 | $tokenMock = $this->getMockBuilder(TokenInterface::class) 68 | ->disableOriginalConstructor() 69 | ->getMock(); 70 | $tokenMock->method('getUser')->willReturn($this->userMock); 71 | 72 | $this->tokenStorageMock = $this->getMockBuilder(TokenStorageInterface::class) 73 | ->disableOriginalConstructor() 74 | ->getMock(); 75 | $this->tokenStorageMock->method('getToken')->willReturn($tokenMock); 76 | } 77 | 78 | protected function setAnonymousUserMocks() 79 | { 80 | $this->userMock = $this->getMockBuilder(AnonymousUserMock::class) 81 | ->getMock(); 82 | 83 | $tokenMock = $this->getMockBuilder(TokenInterface::class) 84 | ->disableOriginalConstructor() 85 | ->getMock(); 86 | $tokenMock->method('getUser')->willReturn($this->userMock); 87 | 88 | $this->tokenStorageMock = $this->getMockBuilder(TokenStorageInterface::class) 89 | ->disableOriginalConstructor() 90 | ->getMock(); 91 | $this->tokenStorageMock->method('getToken')->willReturn($tokenMock); 92 | } 93 | 94 | protected function getRepositoryMock($classMock) 95 | { 96 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 97 | $emRepositoryMock->method('findOneBy')->willReturn($classMock); 98 | 99 | return $emRepositoryMock; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Tests/Service/ClickerTest.php: -------------------------------------------------------------------------------- 1 | setUserMocks(); 29 | 30 | $clicker = new Clicker($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator); 31 | 32 | $this->assertSame(0, $clicker->getClicks(1)); 33 | 34 | $clicks = $clicker->addClick(1); 35 | 36 | $this->assertSame(1, $clicks); 37 | } 38 | 39 | public function testClickWithAnonymous() 40 | { 41 | $this->setAnonymousUserMocks(); 42 | 43 | $clicker = new Clicker($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator); 44 | 45 | $this->assertSame(0, $clicker->getClicks(1)); 46 | 47 | $clicks = $clicker->addClick(1); 48 | 49 | $this->assertSame(1, $clicks); 50 | } 51 | 52 | public function testClickWithAnonymousAndNoIp() 53 | { 54 | $this->setAnonymousUserMocks(); 55 | 56 | $requestMock = $this->getMockBuilder(Request::class) 57 | ->disableOriginalConstructor() 58 | ->setMethods(array('getClientIp')) 59 | ->getMock(); 60 | $requestMock->expects($this->any()) 61 | ->method('getClientIp')->willReturn(null); 62 | 63 | $this->requestStackMock = $this->getMockBuilder(RequestStack::class) 64 | ->disableOriginalConstructor() 65 | ->setMethods(array('getCurrentRequest')) 66 | ->getMock(); 67 | $this->requestStackMock->expects($this->any()) 68 | ->method('getCurrentRequest')->willReturn($requestMock); 69 | 70 | $clicker = new Clicker($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator); 71 | 72 | $this->assertSame(0, $clicker->getClicks(1)); 73 | 74 | $clicks = $clicker->addClick(1); 75 | 76 | $this->assertSame(0, $clicks); 77 | } 78 | 79 | public function testClickWithUserWhoAlreadyClicked() 80 | { 81 | $this->setUserMocks(); 82 | 83 | $clickMock = $this->getClickMock($this->userMock); 84 | $referenceClicksMock = $this->getMockBuilder(ReferenceClicks::class) 85 | ->getMock(); 86 | $referenceClicksMock->method('getReference')->willReturn(1); 87 | $referenceClicksMock->method('getClicks')->willReturn(2); 88 | 89 | $clickEmRepositoryMock = $this->getRepositoryMock($clickMock); 90 | $referenceClicksEmRepositoryMock = $this->getRepositoryMock($referenceClicksMock); 91 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 92 | $this->emMock->expects($this->exactly(2))->method('getRepository')->withConsecutive([Click::class], [ReferenceClicks::class]) 93 | ->willReturnOnConsecutiveCalls($clickEmRepositoryMock, $referenceClicksEmRepositoryMock); 94 | 95 | $clicker = new Clicker($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator); 96 | 97 | $clicks = $clicker->addClick(1); 98 | 99 | $this->assertSame(2, $clicks); 100 | } 101 | 102 | 103 | public function testClickWithUserWhoAlreadyClickedAndNoReference() 104 | { 105 | $this->setUserMocks(); 106 | 107 | $referenceClicksMock = $this->getMockBuilder(ReferenceClicks::class) 108 | ->getMock(); 109 | $referenceClicksMock->method('getReference')->willReturn(1); 110 | 111 | $clickEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 112 | $referenceClicksEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 113 | $clickEmRepositoryMock->expects($this->exactly(1))->method('findOneBy')->willReturn(null); 114 | $referenceClicksEmRepositoryMock->expects($this->exactly(1))->method('findOneBy')->willReturn($referenceClicksMock); 115 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 116 | $this->emMock->expects($this->exactly(2))->method('getRepository')->withConsecutive([Click::class], [ReferenceClicks::class]) 117 | ->willReturnOnConsecutiveCalls($clickEmRepositoryMock, $referenceClicksEmRepositoryMock); 118 | 119 | $clicker = new Clicker($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator); 120 | 121 | $clicks = $clicker->addClick(1); 122 | 123 | $this->assertSame(0, $clicks); 124 | } 125 | 126 | public function testClickWithAnonymousWhoAlreadyClicked() 127 | { 128 | $this->setAnonymousUserMocks(); 129 | 130 | $clickMock = $this->getClickMock($this->userMock); 131 | $referenceClicksMock = $this->getMockBuilder(ReferenceClicks::class) 132 | ->getMock(); 133 | $referenceClicksMock->method('getReference')->willReturn(1); 134 | $referenceClicksMock->method('getClicks')->willReturn(2); 135 | 136 | $clickEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 137 | $referenceClicksEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 138 | $clickEmRepositoryMock->expects($this->exactly(1))->method('findOneBy')->willReturn($clickMock); 139 | $referenceClicksEmRepositoryMock->expects($this->exactly(1))->method('findOneBy')->willReturn($referenceClicksMock); 140 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 141 | $this->emMock->expects($this->exactly(2))->method('getRepository')->withConsecutive([Click::class], [ReferenceClicks::class]) 142 | ->willReturnOnConsecutiveCalls($clickEmRepositoryMock, $referenceClicksEmRepositoryMock); 143 | 144 | $clicker = new Clicker($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator); 145 | 146 | $clicks = $clicker->addClick(1); 147 | 148 | $this->assertSame(2, $clicks); 149 | } 150 | 151 | private function getClickMock($user, $userIP = '127.0.0.1') 152 | { 153 | return $this->getMockBuilder(ClickMock::class) 154 | ->setConstructorArgs(array($user, $userIP)) 155 | ->getMock(); 156 | } 157 | } -------------------------------------------------------------------------------- /Tests/Service/VoterTest.php: -------------------------------------------------------------------------------- 1 | setAnonymousUserMocks(); 32 | 33 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 34 | 35 | $this->assertSame(false, $voter->userCanVoteNegative(1)); 36 | } 37 | 38 | public function testUserCanVoteNegative() 39 | { 40 | $this->setUserMocks(); 41 | 42 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 43 | 44 | $this->assertSame(true, $voter->userCanVoteNegative(1)); 45 | } 46 | 47 | public function testUserCannotVoteNegativeVotedPositive() 48 | { 49 | $this->setUserMocks(); 50 | $votePositiveMock = $this->getVotePositiveMock($this->userMock); 51 | $emRepositoryMock = $this->getRepositoryMock($votePositiveMock); 52 | 53 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 54 | $this->emMock->method('getRepository')->willReturn($emRepositoryMock); 55 | 56 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 57 | 58 | $this->assertSame(false, $voter->userCanVoteNegative(1)); 59 | } 60 | 61 | public function testUserCannotVoteNegativeVotedNegative() 62 | { 63 | $this->setUserMocks(); 64 | $positiveVoteEmRepositoryMock = $this->getRepositoryMock(null); 65 | $voteNegativeMock = $this->getVoteNegativeMock($this->userMock); 66 | $negativeVoteEmRepositoryMock = $this->getRepositoryMock($voteNegativeMock); 67 | 68 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 69 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class]) 70 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock); 71 | 72 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 73 | 74 | $this->assertSame(false, $voter->userCanVoteNegative(1)); 75 | } 76 | 77 | public function testGetVotes() 78 | { 79 | $this->setUserMocks(); 80 | 81 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 82 | 83 | $this->assertSame(0, $voter->getUserPositiveVotes(1)); 84 | $this->assertSame(0, $voter->getPositiveVotes(1)); 85 | $this->assertSame(0, $voter->getNegativeVotes(1)); 86 | $this->assertSame(0, $voter->getAnonymousVotes(1)); 87 | $this->assertSame(false, $voter->getUserVote(1)); 88 | } 89 | 90 | public function testGetVotesWithExistingReference() 91 | { 92 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 93 | ->getMock(); 94 | $referenceVotesMock->method('getReference')->willReturn(1); 95 | $referenceVotesMock->method('isPublished')->willReturn(true); 96 | $referenceVotesMock->method('getPositiveVotes')->willReturn(11); 97 | $referenceVotesMock->method('getUserVotes')->willReturn(6); 98 | $referenceVotesMock->method('getNegativeVotes')->willReturn(3); 99 | $referenceVotesMock->method('getAnonymousVotes')->willReturn(5); 100 | $referenceVotesMock->method('isPublished')->willReturn(true); 101 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 102 | $emRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 103 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 104 | $this->emMock->method('getRepository')->willReturn($emRepositoryMock); 105 | 106 | $this->setUserMocks(); 107 | 108 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 109 | 110 | $this->assertSame($referenceVotesMock, $voter->getUserVote(1)); 111 | $this->assertSame(6, $voter->getUserPositiveVotes(1)); 112 | $this->assertSame(5, $voter->getAnonymousVotes(1)); 113 | $this->assertSame(11, $voter->getPositiveVotes(1)); 114 | $this->assertSame(3, $voter->getNegativeVotes(1)); 115 | $this->assertSame(['irrelevant'], $voter->getNegativeReasons()); 116 | } 117 | 118 | public function testGetUserVoteWhenNegative() 119 | { 120 | $this->setUserMocks(); 121 | $positiveVoteEmRepositoryMock = $this->getRepositoryMock(null); 122 | $voteNegativeMock = $this->getVoteNegativeMock($this->userMock); 123 | $negativeVoteEmRepositoryMock = $this->getRepositoryMock($voteNegativeMock); 124 | 125 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 126 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class]) 127 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock); 128 | 129 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 130 | 131 | $this->assertSame($voteNegativeMock, $voter->getUserVote(1)); 132 | } 133 | 134 | public function testGetAnonymousVoteWithVoteByIP() 135 | { 136 | $this->setAnonymousUserMocks(); 137 | $votePositiveMock = $this->getVotePositiveMock($this->userMock); 138 | $positiveVoteEmRepositoryMock = $this->getRepositoryMock($votePositiveMock); 139 | $negativeVoteEmRepositoryMock = $this->getRepositoryMock(null); 140 | 141 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 142 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class]) 143 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock); 144 | 145 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 146 | 147 | $this->assertSame($votePositiveMock, $voter->getUserVote(1)); 148 | } 149 | 150 | public function testVotePositiveWithUser() 151 | { 152 | $this->setUserMocks(); 153 | 154 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 155 | 156 | $this->assertSame(array('irrelevant'), $voter->getNegativeReasons()); 157 | $this->assertSame(0, $voter->getPositiveVotes(1)); 158 | 159 | $votes = $voter->votePositive(1); 160 | 161 | $this->assertSame(1, $votes); 162 | } 163 | 164 | public function testVotePositiveWithUserAndExistingReference() 165 | { 166 | $this->setUserMocks(); 167 | 168 | $positiveVoteEmRepositoryMock = $this->getRepositoryMock(null); 169 | $negativeVoteEmRepositoryMock = $this->getRepositoryMock(null); 170 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 171 | ->getMock(); 172 | $referenceVotesMock->method('getReference')->willReturn(1); 173 | $referenceVotesMock->method('isPublished')->willReturn(true); 174 | $referenceVotesMock->method('getPositiveVotes')->willReturn(11); 175 | $referenceVotesMock->method('getUserVotes')->willReturn(6); 176 | $referenceVotesMock->method('getNegativeVotes')->willReturn(3); 177 | $referenceVotesMock->method('getAnonymousVotes')->willReturn(5); 178 | $referenceVotesMock->method('isPublished')->willReturn(true); 179 | $referenceVotesEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 180 | $referenceVotesEmRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 181 | 182 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 183 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class], [ReferenceVotes::class]) 184 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock, $referenceVotesEmRepositoryMock); 185 | 186 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 187 | 188 | $votes = $voter->votePositive(1); 189 | 190 | $this->assertSame(11, $votes); 191 | } 192 | 193 | public function testVoteNegativeWithUserAndExistingReference() 194 | { 195 | $this->setUserMocks(); 196 | 197 | $positiveVoteEmRepositoryMock = $this->getRepositoryMock(null); 198 | $negativeVoteEmRepositoryMock = $this->getRepositoryMock(null); 199 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 200 | ->getMock(); 201 | $referenceVotesMock->method('getReference')->willReturn(1); 202 | $referenceVotesMock->method('isPublished')->willReturn(true); 203 | $referenceVotesMock->method('getPositiveVotes')->willReturn(11); 204 | $referenceVotesMock->method('getUserVotes')->willReturn(6); 205 | $referenceVotesMock->method('getNegativeVotes')->willReturn(3); 206 | $referenceVotesMock->method('getAnonymousVotes')->willReturn(5); 207 | $referenceVotesMock->method('isPublished')->willReturn(true); 208 | $referenceVotesEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 209 | $referenceVotesEmRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 210 | 211 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 212 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class], [ReferenceVotes::class]) 213 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock, $referenceVotesEmRepositoryMock); 214 | 215 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 216 | 217 | $votes = $voter->voteNegative(1, 'irrelevant'); 218 | 219 | $this->assertSame(3, $votes); 220 | } 221 | 222 | public function testVoteNegativeWithUser() 223 | { 224 | $referenceVotes = new ReferenceVotes(); 225 | $referenceVotes->setReference(1); 226 | 227 | $this->setUserMocks(); 228 | 229 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 230 | 231 | $this->assertSame(array('irrelevant'), $voter->getNegativeReasons()); 232 | $this->assertSame(0, $voter->getNegativeVotes($referenceVotes->getReference())); 233 | 234 | $votes = $voter->voteNegative(1, 'irrelevant'); 235 | 236 | $this->assertSame(1, $votes); 237 | } 238 | 239 | /** 240 | * @expectedException \Symfony\Component\Finder\Exception\AccessDeniedException 241 | */ 242 | public function testVoteNegativeWithUserWithoutReason() 243 | { 244 | $this->setUserMocks(); 245 | 246 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 247 | 248 | $this->assertSame(array('irrelevant'), $voter->getNegativeReasons()); 249 | $this->assertSame(0, $voter->getNegativeVotes(1)); 250 | 251 | $voter->voteNegative(1, ''); 252 | } 253 | 254 | public function testVotePositiveWithAnonymous() 255 | { 256 | $this->setAnonymousUserMocks(); 257 | 258 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 259 | 260 | $this->assertSame(array('irrelevant'), $voter->getNegativeReasons()); 261 | $this->assertSame(0, $voter->getPositiveVotes(1)); 262 | 263 | $votes = $voter->votePositive(1); 264 | 265 | $this->assertSame(1, $votes); 266 | } 267 | 268 | public function testVoteWithAnonymousOneAllowed() 269 | { 270 | $this->setAnonymousUserMocks(); 271 | 272 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 273 | ->getMock(); 274 | $referenceVotesMock->method('getReference')->willReturn(1); 275 | $referenceVotesMock->method('getPositiveVotes')->willReturn(10); 276 | 277 | $positiveVotesMock = $this->getMockBuilder(VotePositive::class) 278 | ->getMock(); 279 | $positiveVotesMock->method('getReference')->willReturn(1); 280 | 281 | $negativeVotesMock = $this->getMockBuilder(VoteNegative::class) 282 | ->getMock(); 283 | $negativeVotesMock->method('getReference')->willReturn(1); 284 | 285 | $positiveVoteEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 286 | $negativeVoteEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 287 | $referenceVotesEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 288 | $positiveVoteEmRepositoryMock->expects($this->exactly(1))->method('findOneBy')->willReturn(null); 289 | $negativeVoteEmRepositoryMock->method('findOneBy')->willReturn($negativeVotesMock); 290 | $referenceVotesEmRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 291 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 292 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class], [ReferenceVotes::class], [ReferenceVotes::class]) 293 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock, $referenceVotesEmRepositoryMock, $referenceVotesEmRepositoryMock); 294 | 295 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 1, array('irrelevant')); 296 | 297 | $votes = $voter->votePositive(1); 298 | 299 | $this->assertSame(10, $votes); 300 | } 301 | 302 | public function testVoteWithAnonymousTenPercentAllowed() 303 | { 304 | $this->setAnonymousUserMocks(); 305 | 306 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 307 | ->getMock(); 308 | $referenceVotesMock->method('getReference')->willReturn(1); 309 | $referenceVotesMock->method('getAnonymousVotes')->willReturn(1); 310 | $referenceVotesMock->method('getUserVotes')->willReturn(100); 311 | 312 | $positiveVotesMock = $this->getMockBuilder(VotePositive::class) 313 | ->getMock(); 314 | $positiveVotesMock->method('getReference')->willReturn(1); 315 | 316 | $negativeVotesMock = $this->getMockBuilder(VoteNegative::class) 317 | ->getMock(); 318 | $negativeVotesMock->method('getReference')->willReturn(1); 319 | 320 | $positiveVoteEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 321 | $negativeVoteEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 322 | $referenceVotesEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 323 | $positiveVoteEmRepositoryMock->expects($this->exactly(1))->method('findOneBy')->willReturn(null); 324 | $negativeVoteEmRepositoryMock->method('findOneBy')->willReturn($negativeVotesMock); 325 | $referenceVotesEmRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 326 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 327 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class], [ReferenceVotes::class], [ReferenceVotes::class]) 328 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock, $referenceVotesEmRepositoryMock, $referenceVotesEmRepositoryMock); 329 | 330 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 10, 0, array('irrelevant')); 331 | 332 | $voter->votePositive(1); 333 | } 334 | 335 | /** 336 | * @expectedException \Symfony\Component\Finder\Exception\AccessDeniedException 337 | */ 338 | public function testVoteWithAnonymousNotAllowed() 339 | { 340 | $this->setAnonymousUserMocks(); 341 | 342 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 343 | ->getMock(); 344 | $referenceVotesMock->method('getReference')->willReturn(1); 345 | $referenceVotesMock->method('getAnonymousVotes')->willReturn(1); 346 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 347 | $emRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 348 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 349 | $this->emMock->method('getRepository')->willReturn($emRepositoryMock); 350 | 351 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 0, array('irrelevant')); 352 | 353 | $this->assertSame(array('irrelevant'), $voter->getNegativeReasons()); 354 | $this->assertSame(0, $voter->getPositiveVotes(1)); 355 | 356 | $voter->votePositive(1); 357 | } 358 | 359 | /** 360 | * @expectedException \Symfony\Component\Finder\Exception\AccessDeniedException 361 | */ 362 | public function testVotePositiveWithAnonymousPercentToZero() 363 | { 364 | $this->setAnonymousUserMocks(); 365 | 366 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 367 | ->getMock(); 368 | $referenceVotesMock->method('getReference')->willReturn(1); 369 | $referenceVotesMock->method('getAnonymousVotes')->willReturn(10); 370 | $referenceVotesMock->method('getUserVotes')->willReturn(1); 371 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 372 | $emRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 373 | 374 | $positiveVotesMock = $this->getMockBuilder(VotePositive::class) 375 | ->getMock(); 376 | $positiveVotesMock->method('getReference')->willReturn(1); 377 | 378 | $negativeVotesMock = $this->getMockBuilder(VoteNegative::class) 379 | ->getMock(); 380 | $negativeVotesMock->method('getReference')->willReturn(1); 381 | 382 | $positiveVoteEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 383 | $negativeVoteEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 384 | $referenceVotesEmRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 385 | $positiveVoteEmRepositoryMock->expects($this->exactly(1))->method('findOneBy')->willReturn(null); 386 | $negativeVoteEmRepositoryMock->method('findOneBy')->willReturn($negativeVotesMock); 387 | $referenceVotesEmRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 388 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 389 | $this->emMock->method('getRepository')->withConsecutive([VotePositive::class], [VoteNegative::class], [ReferenceVotes::class]) 390 | ->willReturnOnConsecutiveCalls($positiveVoteEmRepositoryMock, $negativeVoteEmRepositoryMock, $referenceVotesEmRepositoryMock); 391 | 392 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 0, 0, array('irrelevant')); 393 | 394 | $voter->votePositive(1); 395 | } 396 | 397 | /** 398 | * @expectedException \Symfony\Component\Finder\Exception\AccessDeniedException 399 | */ 400 | public function testVoteNegativeWithAnonymous() 401 | { 402 | $referenceVotes = new ReferenceVotes(); 403 | $referenceVotes->setReference(1); 404 | 405 | $this->setAnonymousUserMocks(); 406 | 407 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 408 | 409 | $this->assertSame(array('irrelevant'), $voter->getNegativeReasons()); 410 | $this->assertSame(0, $voter->getNegativeVotes($referenceVotes->getReference())); 411 | 412 | $voter->voteNegative(1, 'irrelevant'); 413 | } 414 | 415 | /** 416 | * @expectedException \Symfony\Component\Finder\Exception\AccessDeniedException 417 | */ 418 | public function testVotePositiveWithAnonymousAndNoIp() 419 | { 420 | $referenceVotes = new ReferenceVotes(); 421 | $referenceVotes->setReference(1); 422 | 423 | $requestMock = $this->getMockBuilder(Request::class) 424 | ->disableOriginalConstructor() 425 | ->setMethods(array('getClientIp')) 426 | ->getMock(); 427 | $requestMock->expects($this->any()) 428 | ->method('getClientIp')->willReturn(null); 429 | 430 | $this->requestStackMock = $this->getMockBuilder(RequestStack::class) 431 | ->disableOriginalConstructor() 432 | ->setMethods(array('getCurrentRequest')) 433 | ->getMock(); 434 | $this->requestStackMock->expects($this->any()) 435 | ->method('getCurrentRequest')->willReturn($requestMock); 436 | 437 | $this->setAnonymousUserMocks(); 438 | 439 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 440 | 441 | $this->assertSame(array('irrelevant'), $voter->getNegativeReasons()); 442 | $this->assertSame(0, $voter->getPositiveVotes($referenceVotes->getReference())); 443 | 444 | $voter->votePositive($referenceVotes->getReference()); 445 | } 446 | 447 | public function testPublishReference() 448 | { 449 | $referenceVotesMock = $this->getMockBuilder(ReferenceVotes::class) 450 | ->getMock(); 451 | $referenceVotesMock->method('getReference')->willReturn(1); 452 | $referenceVotesMock->method('isPublished')->willReturn(true); 453 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 454 | $emRepositoryMock->method('findOneBy')->willReturn($referenceVotesMock); 455 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 456 | $this->emMock->method('getRepository')->willReturn($emRepositoryMock); 457 | 458 | $this->setUserMocks(); 459 | 460 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 461 | 462 | $voter->setPublished(1); 463 | $this->assertSame(true, $voter->isPublished(1)); 464 | } 465 | 466 | /** 467 | * @expectedException \Exception 468 | */ 469 | public function testPublishNoReference() 470 | { 471 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 472 | $emRepositoryMock->method('findOneBy')->willReturn(null); 473 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 474 | $this->emMock->method('getRepository')->willReturn($emRepositoryMock); 475 | 476 | $this->setUserMocks(); 477 | 478 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 479 | 480 | $voter->setPublished(1); 481 | } 482 | 483 | public function testIsPublishNoReference() 484 | { 485 | $emRepositoryMock = $this->getMockBuilder(ObjectRepository::class)->getMock(); 486 | $emRepositoryMock->method('findOneBy')->willReturn(null); 487 | $this->emMock = $this->getMockBuilder(EntityManagerInterface::class)->getMock(); 488 | $this->emMock->method('getRepository')->willReturn($emRepositoryMock); 489 | 490 | $this->setUserMocks(); 491 | 492 | $voter = new Voter($this->emMock, $this->tokenStorageMock, $this->requestStackMock, $this->translator, 50, 2, array('irrelevant')); 493 | 494 | $this->assertSame(false, $voter->isPublished(1)); 495 | } 496 | 497 | private function getVotePositiveMock($user, $userIP = '127.0.0.1') 498 | { 499 | return $this->getMockBuilder(VotePositiveMock::class) 500 | ->setConstructorArgs(array($user, $userIP)) 501 | ->getMock(); 502 | } 503 | 504 | private function getVoteNegativeMock($user, $reason = 'irrelevant', $userIP = '127.0.0.1') 505 | { 506 | return $this->getMockBuilder(VoteNegativeMock::class) 507 | ->setConstructorArgs(array($user, $reason, $userIP)) 508 | ->getMock(); 509 | } 510 | 511 | } -------------------------------------------------------------------------------- /Tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | ./Tests 12 | 13 | 14 | 15 | 16 | ./ 17 | 18 | ./MsalsasVotingBundle.php 19 | ./Controller 20 | ./Tests 21 | ./Tests 22 | ./vendor 23 | 24 | 25 | 26 | --------------------------------------------------------------------------------