Commit 20e5f5c1 authored by Thomas Citharel's avatar Thomas Citharel

Merge branch 'develop' into 'master'

Version 1.1.0-beta.3 See merge request !281
parents 6855cf4a cb03c5c0
Pipeline #46404 passed with stage
in 15 seconds
......@@ -17,6 +17,20 @@ test:
paths:
- vendor/
check-trad:
stage: test
allow_failure: true
script:
- if [ -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then echo "*** Unable to check if translations need to be pulled, exiting ***"; exit 1; fi
- export ORIG=$(git diff-files --shortstat)
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make push-locales; fi
- git status > /dev/null 2>&1
- export CHANGES=$(git diff-files --shortstat)
- if [[ $CHANGES != $ORIG ]]; then echo "*** There is changes in locales ***"; echo "*** You need to do `make pull-locales` in your repo ***"; exit 1; fi
only:
- develop
# Create artifacts on master
pages:
stage: deploy
......@@ -28,6 +42,7 @@ pages:
- mkdir tpl_c
- mkdir framadate
- mv `ls -A | grep -v framadate` ./framadate
- chmod -R 644 framadate/ && chmod -R 770 framadate/tpl_c/ && chmod -R 770 framadate/app/inc/
- zip -r latest.zip framadate
- mkdir .public
- cp latest.zip .public
......@@ -45,9 +60,12 @@ beta:
- git checkout develop
- composer install -o --no-interaction --no-progress --prefer-dist --no-dev
- composer dump-autoload --optimize --no-dev --classmap-authoritative
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make pull-locales; fi
- mkdir tpl_c
- mkdir .public
- cp -r * .public
- cp -r .git .public
- mv .public public
- mkdir "${HOME}/.ssh"
- chmod 700 "${HOME}/.ssh"
......
......@@ -79,7 +79,7 @@ if (is_null($message)) {
$subject = '[' . NOMAPPLICATION . '][' . __('EditLink', 'REMINDER') . '] ' . __f('EditLink', 'Edit link for poll "%s"', $poll->title);
//$mailService->send($email, $subject, $body);
$mailService->send($email, $subject, $body);
$sessionService->remove("Common", SESSION_EDIT_LINK_TOKEN);
$sessionService->set("Common", SESSION_EDIT_LINK_TIME, time());
......
......@@ -435,7 +435,7 @@ $smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 8640
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
$smarty->assign('slots_hash', $pollService->hashSlots($slots));
$smarty->assign('votes', $pollService->splitVotes($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes, $poll));
$smarty->assign('comments', $comments);
$smarty->assign('editingVoteId', $editingVoteId);
$smarty->assign('message', $message);
......
......@@ -79,7 +79,7 @@ class PollRepository extends AbstractRepository {
}
/**
* Search polls in databse.
* Search polls in database.
*
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>..., 'mail'=>...]
* @param int $start The number of first entry to select
......@@ -88,28 +88,48 @@ class PollRepository extends AbstractRepository {
*/
public function findAll($search, $start, $limit) {
// Polls
$prepared = $this->prepare('
SELECT p.*,
(SELECT count(1) FROM `' . Utils::table('vote') . '` v WHERE p.id=v.poll_id) votes
FROM `' . Utils::table('poll') . '` p
WHERE (:id = "" OR p.id LIKE :id)
AND (:title = "" OR p.title LIKE :title)
AND (:name = "" OR p.admin_name LIKE :name)
AND (:mail = "" OR p.admin_mail LIKE :mail)
ORDER BY p.title ASC
LIMIT :start, :limit
');
$poll = $search['poll'] . '%';
$title = '%' . $search['title'] . '%';
$name = '%' . $search['name'] . '%';
$mail = '%' . $search['mail'] . '%';
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
$prepared->bindParam(':mail', $mail, PDO::PARAM_STR);
$request = "";
$request .= "SELECT p.*,";
$request .= " (SELECT count(1) FROM `" . Utils::table('vote') . "` v WHERE p.id=v.poll_id) votes";
$request .= " FROM `" . Utils::table('poll') . "` p";
$request .= " WHERE 1";
$values = [];
if (!empty($search["poll"])) {
$request .= " AND p.id LIKE :poll";
$values["poll"] = "{$search["poll"]}%";
}
$fields = [
// key of $search => column name
"title" => "title",
"name" => "admin_name",
"mail" => "admin_mail",
];
foreach ($fields as $searchKey => $columnName) {
if (empty($search[$searchKey])) {
continue;
}
$request .= " AND p.$columnName LIKE :$searchKey";
$values[$searchKey] = "%{$search[$searchKey]}%";
}
$request .= " ORDER BY p.title ASC";
$request .= " LIMIT :start, :limit";
$prepared = $this->prepare($request);
foreach ($values as $searchKey => $value) {
$prepared->bindParam(":$searchKey", $value, PDO::PARAM_STR);
}
$prepared->bindParam(':start', $start, PDO::PARAM_INT);
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
$prepared->execute();
return $prepared->fetchAll();
......
......@@ -4,16 +4,16 @@
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphal DROZ
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est rgi par la licence CeCILL-B. Si une copie de cette licence
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphal DROZ
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Repositories;
......
......@@ -18,6 +18,8 @@
*/
namespace Framadate\Services;
use DateTime;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
/**
* This class helps to clean all inputs from the users or external services.
......@@ -65,7 +67,26 @@ class InputService {
}
public function filterMail($mail) {
return filter_var($mail, FILTER_VALIDATE_EMAIL);
///////////////////////////////////////////////////////////////////////////////////////
// formatting
$mail = trim($mail);
///////////////////////////////////////////////////////////////////////////////////////
// e-mail validation
$resultat = FALSE;
$validator = new EmailValidator();
if ($validator->isValid($mail, new RFCValidation())) {
$resultat = $mail;
}
///////////////////////////////////////////////////////////////////////////////////////
// return
return $resultat;
}
public function filterDescription($description) {
......
......@@ -25,7 +25,6 @@ use Framadate\Form;
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\Token;
use Framadate\Utils;
class PollService {
private $connect;
......@@ -178,8 +177,18 @@ class PollService {
return $this->pollRepository->findAllByAdminMail($mail);
}
function computeBestChoices($votes) {
$result = ['y' => [0], 'inb' => [0]];
/**
* @param array $votes
* @param \stdClass $poll
* @return array
*/
public function computeBestChoices($votes, $poll) {
if (0 === count($votes)) {
return $this->computeEmptyBestChoices($poll);
}
$result = ['y' => [], 'inb' => []];
// if there are votes
foreach ($votes as $vote) {
$choices = str_split($vote->choices);
foreach ($choices as $i => $choice) {
......@@ -262,6 +271,39 @@ class PollService {
return $slots;
}
/**
* @param \stdClass $poll
* @return array
*/
private function computeEmptyBestChoices($poll)
{
$result = ['y' => [], 'inb' => []];
// if there is no votes, calculates the number of slot
$slots = $this->allSlotsByPoll($poll);
if ($poll->format === 'A') {
// poll format classic
for ($i = 0; $i < count($slots); $i++) {
$result['y'][] = 0;
$result['inb'][] = 0;
}
} else {
// poll format date
$slots = $this->splitSlots($slots);
foreach ($slots as $slot) {
for ($i = 0; $i < count($slot->moments); $i++) {
$result['y'][] = 0;
$result['inb'][] = 0;
}
}
}
return $result;
}
private function random($length) {
return Token::getToken($length);
}
......@@ -293,10 +335,10 @@ class PollService {
if (count($votes) <= 0) {
return;
}
$best_choices = $this->computeBestChoices($votes);
$best_choices = $this->computeBestChoices($votes, $poll);
foreach ($best_choices['y'] as $i => $nb_choice) {
// if for this option we have reached maximum value and user wants to add itself too
if ($poll->ValueMax !== null && $nb_choice >= $poll->ValueMax && $user_choice[$i] === "2") {
if ($poll->ValueMax !== null && $nb_choice >= $poll->ValueMax && $user_choice[$i] === "2") {
throw new ConcurrentVoteException();
}
}
......
......@@ -18,7 +18,7 @@
*/
// FRAMADATE version
const VERSION = '1.1.0-alpha.1';
const VERSION = '1.1.0-beta.3';
// PHP Needed version
const PHP_NEEDED_VERSION = '5.6';
......
<?php
namespace Framadate;
abstract class FramaTestCase extends \PHPUnit_Framework_TestCase {
use PHPUnit\Framework\TestCase;
abstract class FramaTestCase extends TestCase {
protected function getTestResourcePath($resourcepath) {
return __DIR__ . '/../resources/' . $resourcepath;
}
......
<?php
namespace Framadate\Services;
use Framadate\FramaTestCase;
class InputServiceUnitTest extends FramaTestCase
{
public function liste_emails() {
return [
// valids addresses
"valid address" => ["example@example.com", "example@example.com"],
"local address" => ["test@localhost", "test@localhost"],
"IP address" => ["ip.email@127.0.0.1", "ip.email@127.0.0.1"],
"with spaces arround" => [" with@spaces ", "with@spaces"],
"unicode caracters" => ["unicode.éà@idn-œ.com", "unicode.éà@idn-œ.com"],
// invalids addresses
"without domain" => ["without-domain", FALSE],
"space inside" => ["example example@example.com", FALSE],
"forbidden chars" => ["special_chars.@example.com", FALSE],
];
}
/**
* @dataProvider liste_emails
*/
public function test_filterMail($email, $expected) {
$inputService = new InputService();
$filtered = $inputService->filterMail($email);
$this->assertSame($expected, $filtered);
}
}
......@@ -6,10 +6,7 @@ use Framadate\FramaTestCase;
class MailServiceUnitTest extends FramaTestCase {
const MSG_KEY = '666';
/**
* @test
*/
function should_send_a_2nd_mail_after_a_good_interval() {
public function test_should_send_a_2nd_mail_after_a_good_interval() {
// Given
$mailService = new MailService(true);
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time() - 1000];
......@@ -21,10 +18,7 @@ class MailServiceUnitTest extends FramaTestCase {
$this->assertSame(true, $canSendMsg);
}
/**
* @test
*/
function should_not_send_2_mails_in_a_short_interval() {
public function test_should_not_send_2_mails_in_a_short_interval() {
// Given
$mailService = new MailService(true);
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time()];
......
......@@ -61,7 +61,8 @@
"phpmailer/phpmailer": "^5.2",
"ircmaxell/password-compat": "dev-master",
"roave/security-advisories": "dev-master",
"erusev/parsedown": "^1.7"
"erusev/parsedown": "^1.7",
"egulias/email-validator": "~2.1"
},
"require-dev": {
......
......@@ -4,9 +4,120 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "a3ca2dd4f87562095f8c19a94560fed7",
"content-hash": "40759eac537218b0212ed923261b9850",
"packages": [
{
"name": "doctrine/lexer",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Lexer\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"lexer",
"parser"
],
"time": "2014-09-09T13:34:57+00:00"
},
{
"name": "egulias/email-validator",
"version": "2.1.3",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
"reference": "1bec00a10039b823cc94eef4eddd47dcd3b2ca04"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/1bec00a10039b823cc94eef4eddd47dcd3b2ca04",
"reference": "1bec00a10039b823cc94eef4eddd47dcd3b2ca04",
"shasum": ""
},
"require": {
"doctrine/lexer": "^1.0.1",
"php": ">= 5.5"
},
"require-dev": {
"dominicsayers/isemail": "dev-master",
"phpunit/phpunit": "^4.8.35",
"satooshi/php-coveralls": "^1.0.1"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Egulias\\EmailValidator\\": "EmailValidator"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Eduardo Gulias Davis"
}
],
"description": "A library for validating emails against several RFCs",
"homepage": "https://github.com/egulias/EmailValidator",
"keywords": [
"email",
"emailvalidation",
"emailvalidator",
"validation",
"validator"
],
"time": "2017-11-15T23:40:40+00:00"
},
{
"name": "erusev/parsedown",
"version": "1.7.1",
"source": {
......@@ -614,60 +725,6 @@
"time": "2015-06-14T21:17:01+00:00"
},
{
"name": "doctrine/lexer",
"version": "v1.0.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Lexer\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"lexer",
"parser"
],
"time": "2014-09-09T13:34:57+00:00"
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v2.10.4",
"source": {
......
......@@ -2,7 +2,62 @@ $(document).ready(function() {
wrapper = new MDEWrapper($('.js-desc textarea')[0], $('#rich-editor-button'), $('#simple-editor-button'));
var firstOpening = true;
$('[data-toggle="popover"]').popover();
function createNode(text) {
var node = document.createElement('pre');
node.style.width = '1px';
node.style.height = '1px';
node.style.position = 'fixed';
node.style.top = '5px';
node.textContent = text;
return node;
}
function copyNode(node) {
var selection = getSelection();
selection.removeAllRanges();
var range = document.createRange();
range.selectNodeContents(node);
selection.addRange(range);
document.execCommand('copy');
selection.removeAllRanges();
}
function copyText(text) {
var node = createNode(text);
document.body.appendChild(node);
copyNode(node);
document.body.removeChild(node);
}
/**
* When clicked on a .clipboard-url link, copy link inside clipboard and show popover confirmation for 2 seconds
*/
$('body').on('click', '.clipboard-url', function(e) {
var btn = $(e.target);
/**
* Kind of workaround for clicking child instead of button (because propagation is stopped with preventDefault())
*/
if (!btn.get(0).hasAttribute('data-toggle')) {
btn = btn.parent();
}
/**
* Try catch because reasons : https://caniuse.com/#feat=clipboard
*/
try {
copyText(btn.attr('href'));
btn.popover('show');
setTimeout(function () {
btn.popover('hide');
}, 2000);
e.preventDefault();
} catch (err) {
console.log('Oops, unable to copy');
}
});
$('#title-form .btn-edit').on('click', function() {
$('#title-form h3').hide();
......@@ -140,4 +195,4 @@ $(document).ready(function() {
});
});
\ No newline at end of file
});
......@@ -75,7 +75,8 @@ $(document).ready(function () {
var lastComment = $('#comments_list')
.find('div.comment')
.last();
lastComment.effect('highlight', {color: 'green'}, 401);
// TODO : replace old jQuery UI Effect with Modern CSS
// lastComment.effect('highlight', {color: 'green'}, 401);
$('html, body').animate({
scrollTop: lastComment.offset().top
}, 750);
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -122,6 +122,7 @@
"Enter a name and a comment!": "Enter a name and a comment!",
"Enter a title": "Enter a title",
"Enter an email address": "Enter an email address",
"Error on amount of voters limitation : value must be an integer greater than 0": "Error on amount of voters limitation: value must be an integer greater than 0",
"Error!": "خطأ !",
"Failed to delete all comments": "Failed to delete all comments",
"Failed to delete all votes": "Failed to delete all votes",
......@@ -142,6 +143,8 @@
"Poll id already used": "Identifier is already used",
"Something is going wrong...": "حدث هناك خطأ ما ...",
"Something is wrong with the format": "Something is wrong with the format",
"Something is wrong with the format: customized urls should only consist of alphanumeric characters and hyphens.": "Something is wrong with the format: Customized urls should only consist of alphanumeric characters and hyphens.",
"Something is wrong with the format: name shouldn't have any spaces before or after": "Something is wrong with the format: name shouldn't have any spaces before or after",
"The address is not correct! You should enter a valid email address (like r.stallman@outlock.com) in order to receive the link to your poll.": "The address is not correct! You should enter a valid email address (like r.stallman@outlock.com) in order to receive the link to your poll.",
"The column already exists": "The column already exists",
"The name is invalid.": "The name is invalid.",
......@@ -182,7 +185,7 @@
"Edit": "تعديل",
"Framadate is an online service for planning an appointment or make a decision quickly and easily.": "Framadate is an online service for planning an appointment or make a decision quickly and easily.",
"Home": "الرئيسية",
"Ifneedbe": "Ifneedbe",
"Ifneedbe": "If need be",
"Legend:": "Legend:",
"Link": "الرابط",
"Make your polls": "Make your polls",
......@@ -269,13 +272,14 @@
"Chart": "المنحنى البياني",
"Display the chart of the results": "عرض المنحنى البياني للنتائج",
"Edit the line: %s": "Edit line: %s",
"Link to edit this particular line": "Link to edit this particular line",
"Remove the line:": "Remove line:",
"Save the choices": "Save choices",
"Scroll to the left": "Scroll to the left",
"Scroll to the right": "Scroll to the right",
"The best choice at this time is:": "The current best choice is:",
"The bests choices at this time are:": "The current best choices are:",
"Vote ifneedbe for": "Vote \"ifneedbe\" for",
"Vote ifneedbe for": "Vote \"If need be\" for",
"Vote no for": "Vote \"no\" for",
"Vote yes for": "Vote \"yes\" for",
"Votes of the poll": "الأصوات",
......
......@@ -122,12 +122,13 @@
"Enter a name and a comment!": "Enankit un anv hag un evezhiadenn!",
"Enter a title": "Ret eo enankañ un titl!",
"Enter an email address": "Ret eo enankañ ur chomlec'h postel",
"Error on amount of voters limitation : value must be an integer greater than 0": "Error on amount of voters limitation: value must be an integer greater than 0",
"Error!": "Fazi!",
"Failed to delete all comments": "Failed to delete all comments",
"Failed to delete all votes": "Failed to delete all votes",
"Failed to delete all comments": "C'hwitadenn en ur zilemel an holl evezhiadennoù",
"Failed to delete all votes": "C'hwitadenn en ur zilemel an holl vouezhioù",
"Failed to delete column": "C'hwitadenn war zilemel ar bann",
"Failed to delete the comment": "Failed to delete the comment",
"Failed to delete the poll": "Failed to delete the poll",
"Failed to delete the comment": "C'hwitadenn en ur zilemel an evezhiadenn",
"Failed to delete the poll": "C'hwitadenn en ur zilemel ar sontadeg",
"Failed to delete the vote!": "C'hwitadenn en ur zilemel ar vouezh!",
"Failed to insert the comment!": "C'hwitadenn en ur enlakaat an evezhiadenn!",
"Failed to save poll": "C'hwitadenn war enrolladenn ar sontadeg",
......@@ -142,6 +143,8 @@
"Poll id already used": "Arveret eo an naoudi endeo",
"Something is going wrong...": "Un dra bennak a-dreuz a zo...",
"Something is wrong with the format": "Un dra bennak a-dreuz a zo gant ar mentrezh",
"Something is wrong with the format: customized urls should only consist of alphanumeric characters and hyphens.": "Something is wrong with the format: Customized urls should only consist of alphanumeric characters and hyphens.",
"Something is wrong with the format: name shouldn't have any spaces before or after": "Something is wrong with the format: name shouldn't have any spaces before or after",
"The address is not correct! You should enter a valid email address (like r.stallman@outlock.com) in order to receive the link to your poll.": "Didalvoudek eo ar chomlec'h postel enanket. Ret eo lakaat ur chomlec'h postel talvoudek (skouer: mari-jo@gozmail.bzh)evit resevout ere ar sontadeg.",
"The column already exists": "Ar bann a zo anezhañ endeo",
"The name is invalid.": "Didalvoudek eo an anv.",
......@@ -269,6 +272,7 @@
"Chart": "Diervad",
"Display the chart of the results": "Skrammañ diervad an disoc'hoù",
"Edit the line: %s": "Kemmañ al linenn: %s",
"Link to edit this particular line": "Link to edit this particular line",
"Remove the line:": "Dilemel al linenn:",
"Save the choices": "Enrollañ an dibaboù",
"Scroll to the left": "Dibunañ d'an tu kleiz",
......@@ -328,7 +332,7 @@
"All voters can modify any vote": "An holl vouezhierien a c'hall kemmañ an holl vouezhioù",
"Customize the URL": "Personelaat an ere",
"Go to step 2": "Mont d'ar bazenn 2",
"Limit the amount of voters per option": "Limit the amount of voters per option",
"Limit the amount of voters per option": "Bevenniñ an niver a vouezhierien dre zibab",
"More informations here:": "Titouroù ouzhpenn amañ:",
"Only the poll maker can see the poll's results": "N'eus nemet krouer ar sontadeg a c'hell gwelet an disoc'hoù",
"Optional parameters": "Arventennoù diret",
......@@ -347,8 +351,8 @@
"To receive an email for each new comment": "Degemer ur postel evit pep evezhiadenn nevez",
"To receive an email for each new vote": "Degemer ur postel evit pep mouezh nevez",
"Use a password to restrict access": "Lakaat ur ger-tremen evit bevenniñ an haeziñ",
"Value Max": "Value Max",
"ValueMax instructions": "voters per options ",