date.chapril.org-framadate/app/classes/Framadate/Services/AdminPollService.php
Paul B dc164c4d79
fix(admin/add_column): stop modifying user input in stored data
An [earlier
change](892fa11373)
modifed the user's input when creating new columns in an existing
Poll.

Indeed all `,` comma were replaced by `-` dashes and the user inputs
was saved as such.

It seems the rational behind it was to keep the existing "formatting"
of log messages where different elements were separated by commas.

This commit makes sure to not strip the users' data but only the
logged message.

Fixes #384
2019-04-15 15:05:26 +02:00

317 lines
10 KiB
PHP

<?php
namespace Framadate\Services;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\Repositories\RepositoryFactory;
/**
* Class AdminPollService
*
* @package Framadate\Services
*/
class AdminPollService {
private $connect;
private $pollService;
private $logService;
private $pollRepository;
private $slotRepository;
private $voteRepository;
private $commentRepository;
function __construct(Connection $connect, PollService $pollService, LogService $logService) {
$this->connect = $connect;
$this->pollService = $pollService;
$this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
$this->voteRepository = RepositoryFactory::voteRepository();
$this->commentRepository = RepositoryFactory::commentRepository();
}
function updatePoll($poll) {
global $config;
if ($poll->end_date > $poll->creation_date) {
return $this->pollRepository->update($poll);
}
return false;
}
/**
* Delete a comment from a poll.
*
* @param $poll_id int The ID of the poll
* @param $comment_id int The ID of the comment
* @return mixed true is action succeeded
*/
function deleteComment($poll_id, $comment_id) {
return $this->commentRepository->deleteById($poll_id, $comment_id);
}
/**
* Remove all comments of a poll.
*
* @param $poll_id int The ID a the poll
* @return bool|null true is action succeeded
*/
function cleanComments($poll_id) {
$this->logService->log("CLEAN_COMMENTS", "id:$poll_id");
return $this->commentRepository->deleteByPollId($poll_id);
}
/**
* Delete a vote from a poll.
*
* @param $poll_id int The ID of the poll
* @param $vote_id int The ID of the vote
* @return mixed true is action succeeded
*/
function deleteVote($poll_id, $vote_id) {
return $this->voteRepository->deleteById($poll_id, $vote_id);
}
/**
* Remove all votes of a poll.
*
* @param $poll_id int The ID of the poll
* @return bool|null true is action succeeded
*/
function cleanVotes($poll_id) {
$this->logService->log('CLEAN_VOTES', 'id:' . $poll_id);
return $this->voteRepository->deleteByPollId($poll_id);
}
/**
* Delete the entire given poll.
*
* @param $poll_id int The ID of the poll
* @return bool true is action succeeded
*/
function deleteEntirePoll($poll_id) {
$poll = $this->pollRepository->findById($poll_id);
$this->logService->log('DELETE_POLL', "id:$poll->id, format:$poll->format, admin:$poll->admin_name, mail:$poll->admin_mail");
// Delete the entire poll
$this->voteRepository->deleteByPollId($poll_id);
$this->commentRepository->deleteByPollId($poll_id);
$this->slotRepository->deleteByPollId($poll_id);
$this->pollRepository->deleteById($poll_id);
return true;
}
/**
* Delete a slot from a poll.
*
* @param object $poll The ID of the poll
* @param object $slot The slot informations (datetime + moment)
* @return bool true if action succeeded
*/
public function deleteDateSlot($poll, $slot) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
$datetime = $slot->title;
$moment = $slot->moment;
$slots = $this->pollService->allSlotsByPoll($poll);
// We can't delete the last slot
if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
return false;
} elseif ($poll->format === 'A' && count($slots) === 1) {
return false;
}
$index = 0;
$indexToDelete = -1;
$newMoments = [];
// Search the index of the slot to delete
foreach ($slots as $aSlot) {
$moments = explode(',', $aSlot->moments);
foreach ($moments as $rowMoment) {
if ($datetime === $aSlot->title) {
if ($moment === $rowMoment) {
$indexToDelete = $index;
} else {
$newMoments[] = $rowMoment;
}
}
$index++;
}
}
// Remove votes
$this->connect->beginTransaction();
$this->voteRepository->deleteByIndex($poll->id, $indexToDelete);
if (count($newMoments) > 0) {
$this->slotRepository->update($poll->id, $datetime, implode(',', $newMoments));
} else {
$this->slotRepository->deleteByDateTime($poll->id, $datetime);
}
$this->connect->commit();
return true;
}
public function deleteClassicSlot($poll, $slot_title) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
$slots = $this->pollService->allSlotsByPoll($poll);
if (count($slots) === 1) {
return false;
}
$index = 0;
$indexToDelete = -1;
// Search the index of the slot to delete
foreach ($slots as $aSlot) {
if ($slot_title === $aSlot->title) {
$indexToDelete = $index;
}
$index++;
}
// Remove votes
$this->connect->beginTransaction();
$this->voteRepository->deleteByIndex($poll->id, $indexToDelete);
$this->slotRepository->deleteByDateTime($poll->id, $slot_title);
$this->connect->commit();
return true;
}
/**
* Add a new slot to a date poll. And insert default values for user's votes.
* <ul>
* <li>Create a new slot if no one exists for the given date</li>
* <li>Create a new moment if a slot already exists for the given date</li>
* </ul>
*
* @param $poll_id int The ID of the poll
* @param $datetime int The datetime
* @param $new_moment string The moment's name
* @throws MomentAlreadyExistsException When the moment to add already exists in database
* @throws \Doctrine\DBAL\ConnectionException
*/
public function addDateSlot($poll_id, $datetime, $new_moment) {
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . str_replace(',', '-', $new_moment));
try {
$slots = $this->slotRepository->listByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime);
} catch (DBALException $e) {
$this->logService->log('ERROR', "Database error, couldn't find slot insert position" . $e->getMessage());
return;
}
try {
// Begin transaction
$this->connect->beginTransaction();
if ($result->slot !== null) {
$slot = $result->slot;
$moments = explode(',', $slot->moments);
// Check if moment already exists (maybe not necessary)
if (in_array($new_moment, $moments, true)) {
throw new MomentAlreadyExistsException();
}
// Update found slot
$moments[] = $new_moment;
$this->slotRepository->update($poll_id, $datetime, implode(',', $moments));
} else {
$this->slotRepository->insert($poll_id, $datetime, $new_moment);
}
$this->voteRepository->insertDefault($poll_id, $result->insert);
// Commit transaction
$this->connect->commit();
} catch (DBALException $e) {
$this->connect->rollBack();
}
}
/**
* Add a new slot to a classic poll. And insert default values for user's votes.
* <ul>
* <li>Create a new slot if no one exists for the given title</li>
* </ul>
*
* @param $poll_id int The ID of the poll
* @param $title int The title
* @throws MomentAlreadyExistsException When the moment to add already exists in database
* @throws \Doctrine\DBAL\ConnectionException
* @throws \Doctrine\DBAL\DBALException
*/
public function addClassicSlot($poll_id, $title) {
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . str_replace(',', '-', $title));
$slots = $this->slotRepository->listByPollId($poll_id);
// Check if slot already exists
$titles = array_map(function ($slot) {
return $slot->title;
}, $slots);
if (in_array($title, $titles, true)) {
// The moment already exists
throw new MomentAlreadyExistsException();
}
// Begin transaction
$this->connect->beginTransaction();
// New slot
$this->slotRepository->insert($poll_id, $title, null);
// Set default votes
$this->voteRepository->insertDefault($poll_id, count($slots));
// Commit transaction
$this->connect->commit();
}
/**
* This method find where to insert a datatime+moment into a list of slots.<br/>
* Return the {insert:X}, where X is the index of the moment into the whole poll (ex: X=0 => Insert to the first column).
* Return {slot:Y}, where Y is not null if there is a slot existing for the given datetime.
*
* @param $slots array All the slots of the poll
* @param $datetime int The datetime of the new slot
* @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
*/
private function findInsertPosition($slots, $datetime) {
$result = new \stdClass();
$result->slot = null;
$result->insert = 0;
// Sort slots before searching where to insert
$this->pollService->sortSlorts($slots);
// Search where to insert new column
foreach ($slots as $k=>$slot) {
$rowDatetime = $slot->title;
$moments = explode(',', $slot->moments);
if ($datetime === $rowDatetime) {
// Here we have to insert at the end of a slot
$result->insert += count($moments);
$result->slot = $slot;
break;
} elseif ($datetime < $rowDatetime) {
// We have to insert before this slot
break;
}
$result->insert += count($moments);
}
return $result;
}
}