diff --git a/src/Badger/Bundle/GameBundle/Controller/BadgeCompletionController.php b/src/Badger/Bundle/GameBundle/Controller/BadgeCompletionController.php index 53b8da5..1615109 100644 --- a/src/Badger/Bundle/GameBundle/Controller/BadgeCompletionController.php +++ b/src/Badger/Bundle/GameBundle/Controller/BadgeCompletionController.php @@ -2,8 +2,10 @@ namespace Badger\Bundle\GameBundle\Controller; +use Badger\Bundle\GameBundle\Entity\VotableItem; use Badger\Bundle\GameBundle\Event\BadgeUnlockEvent; use Badger\Bundle\GameBundle\GameEvents; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -66,6 +68,44 @@ public function rejectAction($id) return $this->redirectToRoute('admin_claimed_badge_index'); } + /** + * Set the badge as votable. + * + * @param string $id + * + * @return RedirectResponse + */ + public function sendToTheCouncilAction($id) + { + $pendingCompletion = $this->get('badger.game.repository.badge_completion') + ->findOneBy(['id' => $id, 'pending' => 1]); + + if (null === $pendingCompletion) { + throw new NotFoundHttpException(sprintf('No pending BadgeCompletion entity with id %s', $id)); + } + + $votableItem = new VotableItem(); + $votableItem->setBadgeCompletion($pendingCompletion); + + try { + $this->get('badger.game.saver.votable_item')->save($votableItem); + } catch (UniqueConstraintViolationException $e) { + $this->addFlash('warning', sprintf( + 'The badge "%s" is already submitted to vote', + $pendingCompletion->getBadge()->getTitle() + )); + + return $this->redirectToRoute('admin_claimed_badge_index'); + } + + $this->addFlash('notice', sprintf( + 'Successfully send the badge "%s" to the people vote! #democracy', + $pendingCompletion->getBadge()->getTitle() + )); + + return $this->redirectToRoute('admin_claimed_badge_index'); + } + /** * Accept a badge completion. * diff --git a/src/Badger/Bundle/GameBundle/Controller/VoteController.php b/src/Badger/Bundle/GameBundle/Controller/VoteController.php new file mode 100644 index 0000000..b406b56 --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Controller/VoteController.php @@ -0,0 +1,124 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + */ +class VoteController extends Controller +{ + /** + * @Route("/votable-badges", name="votable_badges") + * + * @return Response + */ + public function indexAction() + { + $votableItems = $this->get('badger.game.repository.votable_item') + ->findBy(['pending' => 1]); + + $votesResults = []; + foreach ($votableItems as $votableItem) { + $voteResult = 0; + $votes = $votableItem->getVotes(); + foreach ($votes as $vote) { + $voteResult += (int) $vote->getVote(); + } + $votesResults[$votableItem->getId()] = $voteResult; + } + + return $this->render('@Game/pending-votes.html.twig', [ + 'votableItems' => $votableItems, + 'votesResults' => $votesResults + ]); + } + + /** + * @Route("/pending-badge/upvote/{id}", name="badge_upvote") + * + * @return Response + */ + public function upvoteAction($id) + { + $votableItem = $this->get('badger.game.repository.votable_item') + ->findOneBy(['id' => $id, 'pending' => 1]); + + //TODO: to extract + $vote = $this->get('badger.game.repository.vote')->findOneBy( + [ + 'votableItem' => $votableItem, + 'user' => $this->getUser() + ] + ); + + if (null === $vote) { + $vote = new Vote(); + $vote->setVotableItem($votableItem); + $vote->setUser($this->getUser()); + } + + if (1 === $vote->getVote()) { + $vote->setVote(0); + } else { + $vote->setVote(1); + } + + $this->get('badger.game.saver.vote')->save($vote); + + if (null === $votableItem) { + throw new NotFoundHttpException(sprintf('No pending votable entity with id %s', $id)); + } + + $this->addFlash('notice', sprintf( + 'You successfully voted for "%s" to have the badge %s!', + $votableItem->getBadgeCompletion()->getUser()->getUsername(), + $votableItem->getBadgeCompletion()->getBadge()->getTitle() + )); + + return $this->redirectToRoute('votable_badges'); + } + + /** + * @Route("/pending-badge/downvote/{id}", name="badge_downvote") + * + * @return Response + */ + public function downvoteAction($id) + { + $votableItem = $this->get('badger.game.repository.votable_item') + ->findOneBy(['id' => $id, 'pending' => 1]); + + //TODO: to extract + $vote = $this->get('badger.game.repository.vote')->findOneBy( + [ + 'votableItem' => $votableItem, + 'user' => $this->getUser() + ] + ); + + if (null === $vote) { + $vote = new Vote(); + $vote->setVotableItem($votableItem); + $vote->setUser($this->getUser()); + } + + if (-1 === $vote->getVote()) { + $vote->setVote(0); + } else { + $vote->setVote(-1); + } + + $this->get('badger.game.saver.vote')->save($vote); + + $this->addFlash('notice', 'You successfully voted ! "%s" to NOT have the badge %s!'); + + return $this->redirectToRoute('votable_badges'); + } +} diff --git a/src/Badger/Bundle/GameBundle/Doctrine/Repository/VotableItemRepository.php b/src/Badger/Bundle/GameBundle/Doctrine/Repository/VotableItemRepository.php new file mode 100644 index 0000000..03e0bbb --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Doctrine/Repository/VotableItemRepository.php @@ -0,0 +1,15 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + */ +class VotableItemRepository extends EntityRepository +{ +} diff --git a/src/Badger/Bundle/GameBundle/Doctrine/Repository/VoteRepository.php b/src/Badger/Bundle/GameBundle/Doctrine/Repository/VoteRepository.php new file mode 100644 index 0000000..08050c6 --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Doctrine/Repository/VoteRepository.php @@ -0,0 +1,15 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + */ +class VoteRepository extends EntityRepository +{ +} diff --git a/src/Badger/Bundle/GameBundle/Entity/VotableItem.php b/src/Badger/Bundle/GameBundle/Entity/VotableItem.php new file mode 100644 index 0000000..e0e9f0d --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Entity/VotableItem.php @@ -0,0 +1,83 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + */ +class VotableItem +{ + /** @var int */ + private $id; + + /** @var BadgeCompletionInterface */ + private $badgeCompletion; + + /** @var Bool */ + private $pending = true; + + /** @var ArrayCollection */ + private $votes; + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->id; + } + + /** + * @return BadgeCompletionInterface + */ + public function getBadgeCompletion() + { + return $this->badgeCompletion; + } + + /** + * @param BadgeCompletionInterface $badgeCompletion + */ + public function setBadgeCompletion($badgeCompletion) + { + $this->badgeCompletion = $badgeCompletion; + } + + /** + * @return bool + */ + public function isPending() + { + return $this->pending; + } + + /** + * @param bool $pending + */ + public function setPending($pending) + { + $this->pending = $pending; + } + + /** + * @return ArrayCollection + */ + public function getVotes() + { + return $this->votes; + } + + /** + * @param ArrayCollection $votes + */ + public function setVotes($votes) + { + $this->votes = $votes; + } +} diff --git a/src/Badger/Bundle/GameBundle/Entity/Vote.php b/src/Badger/Bundle/GameBundle/Entity/Vote.php new file mode 100644 index 0000000..cba2a74 --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Entity/Vote.php @@ -0,0 +1,87 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + */ +class Vote +{ + /** @var int */ + private $id; + + /** @var UserInterface */ + protected $user; + + /** @var VotableItem */ + protected $votableItem; + + /** + * @return VotableItem + */ + public function getVotableItem() + { + return $this->votableItem; + } + + /** + * @param VotableItem $votableItem + */ + public function setVotableItem($votableItem) + { + $this->votableItem = $votableItem; + } + + /** @var int */ + protected $vote; + + public function __construct() + { + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function getUser() + { + return $this->user; + } + + /** + * {@inheritdoc} + */ + public function setUser($user) + { + $this->user = $user; + } + + /** + * @return int + */ + public function getVote() + { + return $this->vote; + } + + /** + * @param int $vote + */ + public function setVote($vote) + { + $this->vote = $vote; + } +} diff --git a/src/Badger/Bundle/GameBundle/Factory/TagFactory.php b/src/Badger/Bundle/GameBundle/Factory/TagFactory.php index 5dca332..c822b22 100644 --- a/src/Badger/Bundle/GameBundle/Factory/TagFactory.php +++ b/src/Badger/Bundle/GameBundle/Factory/TagFactory.php @@ -3,7 +3,7 @@ namespace Badger\Bundle\GameBundle\Factory; use Badger\Bundle\GameBundle\Entity\Tag; -use Badger\Component\Tag\Factory\TagFactoryInterface; +use Badger\Component\Game\Factory\TagFactoryInterface; /** * @author Adrien Pétremann diff --git a/src/Badger/Bundle/GameBundle/Resources/config/doctrine/VotableItem.orm.yml b/src/Badger/Bundle/GameBundle/Resources/config/doctrine/VotableItem.orm.yml new file mode 100644 index 0000000..a449f2e --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Resources/config/doctrine/VotableItem.orm.yml @@ -0,0 +1,29 @@ +Badger\Bundle\GameBundle\Entity\VotableItem: + type: entity + table: votableitem + repositoryClass: Badger\Bundle\GameBundle\Doctrine\Repository\VotableItemRepository + fields: + id: + type: guid + id: true + generator: + strategy: UUID + pending: + type: boolean + nullable: false + oneToOne: + badgeCompletion: + targetEntity: Badger\Bundle\GameBundle\Entity\BadgeCompletion + joinColumn: + badge_id: + referencedColumnName: id + onDelete: cascade + oneToMany: + votes: + targetEntity: Badger\Bundle\GameBundle\Entity\Vote + mappedBy: votableItem + cascade: + - persist + - detach + - remove + orphanRemoval: true diff --git a/src/Badger/Bundle/GameBundle/Resources/config/doctrine/Vote.orm.yml b/src/Badger/Bundle/GameBundle/Resources/config/doctrine/Vote.orm.yml new file mode 100644 index 0000000..d2a4fe6 --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Resources/config/doctrine/Vote.orm.yml @@ -0,0 +1,28 @@ +Badger\Bundle\GameBundle\Entity\Vote: + type: entity + table: vote + repositoryClass: Badger\Bundle\GameBundle\Doctrine\Repository\VoteRepository + fields: + id: + type: guid + id: true + generator: + strategy: UUID + vote: + type: integer + oneToOne: + user: + targetEntity: Badger\Bundle\UserBundle\Entity\User + cascade: ['persist'] + joinColumn: + user_id: + referencedColumnName: id + onDelete: cascade + manyToOne: + votableItem: + targetEntity: Badger\Bundle\GameBundle\Entity\VotableItem + inversedBy: votes + joinColumn: + name: vote_id + referencedColumnName: id + onDelete: cascade diff --git a/src/Badger/Bundle/GameBundle/Resources/config/repositories.yml b/src/Badger/Bundle/GameBundle/Resources/config/repositories.yml index 399dd21..16fdaa2 100644 --- a/src/Badger/Bundle/GameBundle/Resources/config/repositories.yml +++ b/src/Badger/Bundle/GameBundle/Resources/config/repositories.yml @@ -7,6 +7,8 @@ parameters: badger.game.repository.adventure_step.class: Badger\Bundle\GameBundle\Doctrine\Repository\AdventureStepRepository badger.game.repository.adventure_step_completion.class: Badger\Bundle\GameBundle\Doctrine\Repository\AdventureStepCompletionRepository badger.game.repository.tag.class: Badger\Bundle\GameBundle\Doctrine\Repository\TagRepository + badger.game.repository.votable_item.class: Badger\Bundle\GameBundle\Doctrine\Repository\VotableItemRepository + badger.game.repository.vote.class: Badger\Bundle\GameBundle\Doctrine\Repository\VoteRepository services: badger.game.repository.badge: @@ -56,3 +58,15 @@ services: factory: ['@doctrine.orm.entity_manager', 'getRepository'] arguments: - Badger\Bundle\GameBundle\Entity\Tag + + badger.game.repository.votable_item: + class: '%badger.game.repository.votable_item.class%' + factory: ['@doctrine.orm.entity_manager', 'getRepository'] + arguments: + - Badger\Bundle\GameBundle\Entity\VotableItem + + badger.game.repository.vote: + class: '%badger.game.repository.vote.class%' + factory: ['@doctrine.orm.entity_manager', 'getRepository'] + arguments: + - Badger\Bundle\GameBundle\Entity\Vote diff --git a/src/Badger/Bundle/GameBundle/Resources/config/routing/claimed-badge.yml b/src/Badger/Bundle/GameBundle/Resources/config/routing/claimed-badge.yml index 09e471d..b6d1c55 100644 --- a/src/Badger/Bundle/GameBundle/Resources/config/routing/claimed-badge.yml +++ b/src/Badger/Bundle/GameBundle/Resources/config/routing/claimed-badge.yml @@ -8,6 +8,11 @@ admin_claimed_badge_reject: defaults: { _controller: "GameBundle:BadgeCompletion:reject" } methods: GET +admin_claimed_badge_send_to_vote: + path: /{id}/send-to-vote + defaults: { _controller: "GameBundle:BadgeCompletion:sendToTheCouncil" } + methods: GET + admin_claimed_badge_accept: path: /{id}/accept defaults: { _controller: "GameBundle:BadgeCompletion:accept" } diff --git a/src/Badger/Bundle/GameBundle/Resources/config/savers.yml b/src/Badger/Bundle/GameBundle/Resources/config/savers.yml index 070964a..ad29e05 100644 --- a/src/Badger/Bundle/GameBundle/Resources/config/savers.yml +++ b/src/Badger/Bundle/GameBundle/Resources/config/savers.yml @@ -44,3 +44,15 @@ services: arguments: - '@doctrine.orm.entity_manager' - 'Badger\Bundle\GameBundle\Entity\Tag' + + badger.game.saver.votable_item: + class: '%badger.storage_utils.saver.base.class%' + arguments: + - '@doctrine.orm.entity_manager' + - 'Badger\Bundle\GameBundle\Entity\VotableItem' + + badger.game.saver.vote: + class: '%badger.storage_utils.saver.base.class%' + arguments: + - '@doctrine.orm.entity_manager' + - 'Badger\Bundle\GameBundle\Entity\Vote' diff --git a/src/Badger/Bundle/GameBundle/Resources/views/base.html.twig b/src/Badger/Bundle/GameBundle/Resources/views/base.html.twig index 10e1851..e8118f2 100644 --- a/src/Badger/Bundle/GameBundle/Resources/views/base.html.twig +++ b/src/Badger/Bundle/GameBundle/Resources/views/base.html.twig @@ -107,6 +107,9 @@
  • Leaderboard
  • +
  • + Council +
  • diff --git a/src/Badger/Bundle/GameBundle/Resources/views/claimed-badges/index.html.twig b/src/Badger/Bundle/GameBundle/Resources/views/claimed-badges/index.html.twig index a68846a..12f33c9 100644 --- a/src/Badger/Bundle/GameBundle/Resources/views/claimed-badges/index.html.twig +++ b/src/Badger/Bundle/GameBundle/Resources/views/claimed-badges/index.html.twig @@ -62,6 +62,9 @@ + + + diff --git a/src/Badger/Bundle/GameBundle/Resources/views/council/index.html.twig b/src/Badger/Bundle/GameBundle/Resources/views/council/index.html.twig new file mode 100644 index 0000000..a68846a --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Resources/views/council/index.html.twig @@ -0,0 +1,85 @@ +{% extends 'GameBundle::base-admin.html.twig' %} + +{% block body %} + + + {% for flash_message in app.session.flashbag.get('notice') %} +
    + + {{ flash_message }} +
    + {% endfor %} + + {% for flash_message in app.session.flashbag.get('error') %} +
    + + {{ flash_message }} +
    + {% endfor %} + + {% if pendingCompletions is not empty %} +
    +
    + + + + + + + + + + + {% for pendingCompletion in pendingCompletions %} + + + + + + + {% endfor %} + +
    UserBadgeClaimed dateAccept that claim?
    + + {{ pendingCompletion.user.username }} + + + + {{ pendingCompletion.badge.title }} + {{ pendingCompletion.badge.description|nl2br }} + + + {{ pendingCompletion.completionDate|date("d/m/Y") }} + + +
    +
    +
    + {% else %} +
    +
    + No claim, no pain. +
    + {% endif %} +{% endblock %} + +{% block javascripts %} + + + +{% endblock %} diff --git a/src/Badger/Bundle/GameBundle/Resources/views/pending-votes.html.twig b/src/Badger/Bundle/GameBundle/Resources/views/pending-votes.html.twig new file mode 100644 index 0000000..47d0995 --- /dev/null +++ b/src/Badger/Bundle/GameBundle/Resources/views/pending-votes.html.twig @@ -0,0 +1,54 @@ +{% extends '@Game/base.html.twig' %} + +{% block body %} +
    +
    + {% for item in votableItems %} +
    +
    +
    {{ item.badgeCompletion.user.username|upper }} claimed the "{{ item.badgeCompletion.badge.title }}" badge, does he deserve it ?
    +
    +
    + + + +
    + +
    +
    + {{ item.badgeCompletion.badge.title }} +
    + + {{ item.badgeCompletion.badge.description }} + + + {{ votesResults[item.id] }} + +
    +
    + + +
    +
    +
    +
    + {% endfor %} +{% endblock %} + +{% block javascripts %} + +{% endblock %}