Merge branch 'develop' into 'develop'

Add search engine on admin polls list

3 fields : id, title, author

See merge request !41
This commit is contained in:
Olivier PEREZ 2015-03-29 16:04:16 +02:00
commit ce6c1bd820
17 changed files with 230 additions and 88 deletions

View File

@ -22,13 +22,24 @@ use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\SecurityService;
use Framadate\Services\SuperAdminService;
use Framadate\Utils;
include_once __DIR__ . '/../app/inc/init.php';
include_once __DIR__ . '/../bandeaux.php';
const POLLS_PER_PAGE = 30;
/* Functions */
function buildSearchQuery($search) {
$query = '';
foreach ($search as $key => $value) {
$query .= $key . '=' . urlencode($value) . '&';
}
return substr($query, 0, -1);
}
/* --------- */
/* Variables */
/* --------- */
@ -49,6 +60,11 @@ $securityService = new SecurityService();
$page = (int)filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT);
$page = ($page >= 1) ? $page : 1;
// Search
$search['poll'] = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$search['title'] = filter_input(INPUT_GET, 'title', FILTER_SANITIZE_STRING);
$search['name'] = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_STRING);
/* PAGE */
/* ---- */
@ -63,17 +79,21 @@ if (!empty($_POST['delete_confirm']) && $securityService->checkCsrf('admin', $_P
$adminPollService->deleteEntirePoll($poll_id);
}
$found = $superAdminService->findAllPolls($page-1, POLLS_PER_PAGE);
$found = $superAdminService->findAllPolls($search, $page - 1, POLLS_PER_PAGE);
$polls = $found['polls'];
$count = $found['count'];
$total = $found['total'];
// Assign data to template
$smarty->assign('polls', $polls);
$smarty->assign('count', $count);
$smarty->assign('total', $total);
$smarty->assign('page', $page);
$smarty->assign('pages', ceil($count / POLLS_PER_PAGE));
$smarty->assign('poll_to_delete', $poll_to_delete);
$smarty->assign('crsf', $securityService->getToken('admin'));
$smarty->assign('search', $search);
$smarty->assign('search_query', buildSearchQuery($search));
$smarty->assign('title', __('Admin\\Polls'));

View File

@ -277,29 +277,46 @@ class FramaDB {
}
/**
* @param $start int The index of the first poll to return
* @param $limit int The limit size
* @return array
* Search polls in databse.
*
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @return array The found polls
*/
public function findAllPolls($start, $limit) {
public function findAllPolls($search) {
// 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
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)
ORDER BY p.title ASC
LIMIT :start, :limit');
$prepared->bindParam(':start', $start, PDO::PARAM_INT);
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
$prepared->execute();
$polls = $prepared->fetchAll();
');
$poll = $search['poll'] . '%';
$title = '%' . $search['title'] . '%';
$name = '%' . $search['name'] . '%';
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
$prepared->execute();
return $prepared->fetchAll();
}
/**
* Get the total number of polls in databse.
*
* @return int The number of polls
*/
public function countPolls() {
// Total count
$stmt = $this->query('SELECT count(1) nb FROM `' . Utils::table('poll') . '`');
$count = $stmt->fetch();
$stmt->closeCursor();
return ['polls' => $polls, 'count' => $count->nb];
return $count->nb;
}
public function countVotesByPollId($poll_id) {

View File

@ -19,13 +19,18 @@ class SuperAdminService {
/**
* Return the list of all polls.
*
* @param $page int The page index (O = first page)
* @param $limit int The limit size
* @return array ['polls' => The {$limit} polls, 'count' => Total count]
* polls, 'count' => Total count]
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @param int $page The page index (O = first page)
* @param int $limit The limit size
* @return array ['polls' => The {$limit} polls, 'count' => Entries found by the query, 'total' => Total count]
*/
public function findAllPolls($page, $limit) {
return $this->connect->findAllPolls($page * $limit, $limit);
public function findAllPolls($search, $page, $limit) {
$start = $page * $limit;
$polls = $this->connect->findAllPolls($search);
$total = $this->connect->countPolls();
return ['polls' => array_slice($polls, $start, $limit), 'count' => count($polls), 'total' => $total];
}
}

8
composer.lock generated
View File

@ -12,12 +12,12 @@
"source": {
"type": "git",
"url": "https://github.com/olivierperez/o80-i18n.git",
"reference": "45335ac7a2d24d8ed85861f737758c15cd4f52f6"
"reference": "830112445b557068b9fe02b576d5cf770fcdf037"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/olivierperez/o80-i18n/zipball/45335ac7a2d24d8ed85861f737758c15cd4f52f6",
"reference": "45335ac7a2d24d8ed85861f737758c15cd4f52f6",
"url": "https://api.github.com/repos/olivierperez/o80-i18n/zipball/830112445b557068b9fe02b576d5cf770fcdf037",
"reference": "830112445b557068b9fe02b576d5cf770fcdf037",
"shasum": ""
},
"require": {
@ -50,7 +50,7 @@
"internationalization",
"php"
],
"time": "2015-03-21 22:37:40"
"time": "2015-03-27 19:37:27"
},
{
"name": "smarty/smarty",

View File

@ -358,3 +358,8 @@ table.results .btn-link.btn-sm {
#md-a-imgModalLabel {
font-size: 24px;
}
/* Admin */
#poll_search {
cursor: pointer;
}

23
js/app/admin/polls.js Normal file
View File

@ -0,0 +1,23 @@
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* 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 Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
*
* =============================
*
* 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 Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
$(document).ready(function () {
$('#poll_search .panel-heading').on('click', function (e) {
$('#poll_search .panel-body').slideToggle('fast');
});
});

View File

@ -1,12 +1,31 @@
$(document).ready(function() {
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* 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 Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
*
* =============================
*
* 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 Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
$("#poll_form").submit(function( event ) {
$(document).ready(function () {
$(".check-name").click(function (event) {
var name = $("#name").val();
var regexContent = $("#parameter_name_regex").text().split("/");
var regex = new RegExp(regexContent[1], regexContent[2]);
if (name.length == 0 || !regex.test(name)) {
event.preventDefault();
var newMessage = $("#nameErrorMessage").clone();
var newMessage = $("#nameErrorMessage").clone();
$("#message-container").empty();
$("#message-container").append(newMessage);
newMessage.removeClass("hidden");

View File

@ -34,7 +34,8 @@
"Page generated in": "Seite generiert in",
"seconds": "Sekunden",
"Choice": "Wahl",
"Link": "Link"
"Link": "Link",
"Search": "Suche"
},
"Date": {
"dd/mm/yyyy": "jj/mm/aaaa",
@ -243,7 +244,7 @@
"Author": "Autor",
"Email": "E-Mail-Adresse",
"Expiration date": "Verfallsdatum",
"Users": "Nutzer",
"Votes": "Stimmen",
"Actions": "Aktionen",
"See the poll": "Umfrage sehen",
"Change the poll": "Umfrage ändern",

View File

@ -34,7 +34,8 @@
"Page generated in": "Page generated in",
"seconds": "seconds",
"Choice": "Choice",
"Link": "Link"
"Link": "Link",
"Search": "Search"
},
"Date" : {
"dd/mm/yyyy": "jj/mm/aaaa",
@ -244,7 +245,7 @@
"Author": "Author",
"Email": "Courriel",
"Expiration date": "Date d'expiration",
"Users": "Users",
"Votes": "Votes",
"Actions": "Actions",
"See the poll": "See the poll",
"Change the poll": "Change the poll",

View File

@ -34,7 +34,8 @@
"Page generated in": "ES_Page générée en",
"seconds": "ES_secondes",
"Choice": "Opciòn",
"Link": "ES_Lien"
"Link": "ES_Lien",
"Search": "Búsqueda"
},
"Date": {
"dd/mm/yyyy": "ES_jj/mm/aaaa",
@ -243,7 +244,7 @@
"Author": "ES_Auteur",
"Email": "ES_Courriel",
"Expiration date": "ES_Date d'expiration",
"Users": "ES_Utilisateurs",
"Votes": "Votos",
"Actions": "Acciones",
"See the poll": "Ver la encuesta",
"Change the poll": "Cambiar la encuesta",

View File

@ -34,7 +34,8 @@
"Page generated in": "Page générée en",
"seconds": "secondes",
"Choice": "Choix",
"Link": "Lien"
"Link": "Lien",
"Search": "Chercher"
},
"Date": {
"dd/mm/yyyy": "jj/mm/aaaa",
@ -242,8 +243,8 @@
"Title": "Titre",
"Author": "Auteur",
"Email": "Courriel",
"Expiration date": "Date d'expiration",
"Users": "Utilisateurs",
"Expiration date": "Expiration",
"Votes": "Votes",
"Actions": "Actions",
"See the poll": "Voir le sondage",
"Change the poll": "Modifier le sondage",

View File

@ -1,6 +1,40 @@
{extends 'admin/admin_page.tpl'}
{block name="header"}
<script src="{"js/app/admin/polls.js"|resource}" type="text/javascript"></script>
{/block}
{block 'admin_main'}
<div class="panel panel-default" id="poll_search">
<div class="panel-heading">{__('Generic\\Search')}</div>
<div class="panel-body" style="display: none;">
<form action="" method="GET">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="poll" class="control-label">{__('Admin\\Poll ID')}</label>
<input type="text" name="poll" id="poll" class="form-control"
value="{$search['poll']|html}"/>
</div>
<div class="form-group">
<label for="name" class="control-label">{__('Admin\\Author')}</label>
<input type="text" name="name" id="name" class="form-control"
value="{$search['name']|html}"/>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="title" class="control-label">{__('Admin\\Title')}</label>
<input type="text" name="title" id="title" class="form-control"
value="{$search['title']|html}"/>
</div>
</div>
</div>
<input type="submit" value="{__('Generic\\Search')}" class="btn btn-default"/>
</form>
</div>
</div>
<form action="" method="POST">
<input type="hidden" name="csrf" value="{$crsf}"/>
{if $poll_to_delete}
@ -15,13 +49,13 @@
</p>
</div>
{/if}
<input type="hidden" name="csrf" value="{$crsf}"/>
<div class="panel panel-default">
<div class="panel-heading">
{$polls|count} / {$count} {__('Admin\\polls in the database at this time')}
{$count} / {$total} {__('Admin\\polls in the database at this time')}
</div>
<table class="table table-bordered table-polls">
<tr align="center">
<th scope="col"></th>
@ -29,7 +63,7 @@
<th scope="col">{__('Admin\\Author')}</th>
<th scope="col">{__('Admin\\Email')}</th>
<th scope="col">{__('Admin\\Expiration date')}</th>
<th scope="col">{__('Admin\\Users')}</th>
<th scope="col">{__('Admin\\Votes')}</th>
<th scope="col">{__('Admin\\Poll ID')}</th>
<th scope="col" colspan="3">{__('Admin\\Actions')}</th>
</tr>
@ -37,9 +71,13 @@
<tr align="center">
<td class="cell-format">
{if $poll->format === 'D'}
<span class="glyphicon glyphicon-calendar" aria-hidden="true" title="{__('Generic\\Date')}"></span><span class="sr-only">{__('Generic\\Date')}</span>
<span class="glyphicon glyphicon-calendar" aria-hidden="true"
title="{__('Generic\\Date')}"></span>
<span class="sr-only">{__('Generic\\Date')}</span>
{else}
<span class="glyphicon glyphicon-list-alt" aria-hidden="true" title="{__('Generic\\Classic')}"></span><span class="sr-only">{__('Generic\\Classic')}</span>
<span class="glyphicon glyphicon-list-alt" aria-hidden="true"
title="{__('Generic\\Classic')}"></span>
<span class="sr-only">{__('Generic\\Classic')}</span>
{/if}
</td>
<td>{$poll->title|html}</td>
@ -47,15 +85,26 @@
<td>{$poll->admin_mail|html}</td>
{if strtotime($poll->end_date) > time()}
<td>{date('d/m/y', strtotime($poll->end_date))}</td>
<td>{date('d/m/y', strtotime($poll->end_date))}</td>
{else}
<td><span class="text-danger">{strtotime($poll->end_date)|date_format:'d/m/Y'}</span></td>
<td><span class="text-danger">{strtotime($poll->end_date)|date_format:'d/m/Y'}</span></td>
{/if}
<td>{$poll->votes|html}</td>
<td>{$poll->id|html}</td>
<td><a href="{$poll->id|poll_url|html}" class="btn btn-link" title="{__('Admin\\See the poll')}"><span class="glyphicon glyphicon-eye-open"></span><span class="sr-only">{__('Admin\\See the poll')}</span></a></td>
<td><a href="{$poll->admin_id|poll_url:true|html}" class="btn btn-link" title="{__('Admin\\Change the poll')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{__('Admin\\Change the poll')}</span></a></td>
<td><button type="submit" name="delete_poll" value="{$poll->id|html}" class="btn btn-link" title="{__('Admin\\Deleted the poll')}"><span class="glyphicon glyphicon-trash text-danger"></span><span class="sr-only">{__('Admin\\Deleted the poll')}</span></td>
<td><a href="{$poll->id|poll_url|html}" class="btn btn-link"
title="{__('Admin\\See the poll')}"><span
class="glyphicon glyphicon-eye-open"></span><span
class="sr-only">{__('Admin\\See the poll')}</span></a></td>
<td><a href="{$poll->admin_id|poll_url:true|html}" class="btn btn-link"
title="{__('Admin\\Change the poll')}"><span
class="glyphicon glyphicon-pencil"></span><span
class="sr-only">{__('Admin\\Change the poll')}</span></a></td>
<td>
<button type="submit" name="delete_poll" value="{$poll->id|html}" class="btn btn-link"
title="{__('Admin\\Deleted the poll')}"><span
class="glyphicon glyphicon-trash text-danger"></span><span
class="sr-only">{__('Admin\\Deleted the poll')}</span>
</td>
</tr>
{/foreach}
</table>
@ -64,9 +113,10 @@
{__('Admin\\Pages:')}
{for $p=1 to $pages}
{if $p===$page}
<a href="{$SERVER_URL}admin/polls.php?page={$p}" class="btn btn-danger" disabled="disabled">{$p}</a>
<a href="{$SERVER_URL}admin/polls.php?page={$p}&{$search_query}" class="btn btn-danger"
disabled="disabled">{$p}</a>
{else}
<a href="{$SERVER_URL}admin/polls.php?page={$p}" class="btn btn-info">{$p}</a>
<a href="{$SERVER_URL}admin/polls.php?page={$p}&{$search_query}" class="btn btn-info">{$p}</a>
{/if}
{/for}
</div>

View File

@ -1,4 +0,0 @@
</main>
</div> <!-- .container -->
</body>
</html>

View File

@ -1,31 +0,0 @@
<!DOCTYPE html>
<html lang="{$html_lang}">
<head>
<meta charset="utf-8">
{if !empty($title)}
<title>{$title|html} - {$APPLICATION_NAME|html}</title>
{else}
<title>{$APPLICATION_NAME|html}</title>
{/if}
<link rel="stylesheet" href="{'css/bootstrap.min.css'|resource}">
<link rel="stylesheet" href="{'css/datepicker3.css'|resource}">
<link rel="stylesheet" href="{'css/style.css'|resource}">
<link rel="stylesheet" href="{'css/frama.css'|resource}">
<link rel="stylesheet" href="{'css/print.css'|resource}" media="print">
<script type="text/javascript" src="{'js/jquery-1.11.1.min.js'|resource}"></script>
<script type="text/javascript" src="{'js/bootstrap.min.js'|resource}"></script>
<script type="text/javascript" src="{'js/bootstrap-datepicker.js'|resource}"></script>
<script type="text/javascript" src="{"js/locales/bootstrap-datepicker.$html_lang.js"|resource}"></script>
<script type="text/javascript" src="{'js/core.js'|resource}"></script>
{if !empty($nav_js)}
<script src="{'nav/nav.js'|resource}" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->
{/if}
{block name="header"}{/block}
</head>
<body>
<div class="container ombre">

View File

@ -1,6 +1,40 @@
{include file='head.tpl'}
<!DOCTYPE html>
<html lang="{$html_lang}">
<head>
<meta charset="utf-8">
{if !empty($title)}
<title>{$title|html} - {$APPLICATION_NAME|html}</title>
{else}
<title>{$APPLICATION_NAME|html}</title>
{/if}
<link rel="stylesheet" href="{'css/bootstrap.min.css'|resource}">
<link rel="stylesheet" href="{'css/datepicker3.css'|resource}">
<link rel="stylesheet" href="{'css/style.css'|resource}">
<link rel="stylesheet" href="{'css/frama.css'|resource}">
<link rel="stylesheet" href="{'css/print.css'|resource}" media="print">
<script type="text/javascript" src="{'js/jquery-1.11.1.min.js'|resource}"></script>
<script type="text/javascript" src="{'js/bootstrap.min.js'|resource}"></script>
<script type="text/javascript" src="{'js/bootstrap-datepicker.js'|resource}"></script>
<script type="text/javascript" src="{"js/locales/bootstrap-datepicker.$html_lang.js"|resource}"></script>
<script type="text/javascript" src="{'js/core.js'|resource}"></script>
{if !empty($nav_js)}
<script src="{'nav/nav.js'|resource}" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->
{/if}
{block name="header"}{/block}
</head>
<body>
<div class="container ombre">
{include file='header.tpl'}
{block name=main}{/block}
{include file='footer.tpl'}
</main>
</div> <!-- .container -->
</body>
</html>

View File

@ -69,7 +69,7 @@
</ul>
</td>
{/foreach}
<td style="padding:5px"><button type="submit" class="btn btn-success btn-xs" name="save" value="{$vote->id|html}" title="{__('Poll results\\Save the choices')} {$vote->name|html}">{__('Generic\\Save')}</button></td>
<td style="padding:5px"><button type="submit" class="btn btn-success btn-xs check-name" name="save" value="{$vote->id|html}" title="{__('Poll results\\Save the choices')} {$vote->name|html}">{__('Generic\\Save')}</button></td>
{else}
{* Voted line *}
@ -140,7 +140,7 @@
</ul>
</td>
{/foreach}
<td><button type="submit" class="btn btn-success btn-md" name="save" title="{__('Poll results\\Save the choices')}">{__('Generic\\Save')}</button></td>
<td><button type="submit" class="btn btn-success btn-md check-name" name="save" title="{__('Poll results\\Save the choices')}">{__('Generic\\Save')}</button></td>
</tr>
{/if}

View File

@ -115,7 +115,7 @@
</ul>
</td>
{/foreach}
<td style="padding:5px"><button type="submit" class="btn btn-success btn-xs" name="save" value="{$vote->id|html}" title="{__('Poll results\\Save the choices')} {$vote->name|html}">{__('Generic\\Save')}</button></td>
<td style="padding:5px"><button type="submit" class="btn btn-success btn-xs check-name" name="save" value="{$vote->id|html}" title="{__('Poll results\\Save the choices')} {$vote->name|html}">{__('Generic\\Save')}</button></td>
{else}
{* Voted line *}
@ -190,7 +190,7 @@
{$i = $i+1}
{/foreach}
{/foreach}
<td><button type="submit" class="btn btn-success btn-md" name="save" title="{__('Poll results\\Save the choices')}">{__('Generic\\Save')}</button></td>
<td><button type="submit" class="btn btn-success btn-md check-name" name="save" title="{__('Poll results\\Save the choices')}">{__('Generic\\Save')}</button></td>
</tr>
{/if}