Mise à jour 13501

This commit is contained in:
Fred Tempez 2024-12-29 18:05:27 +01:00
parent 12b35302af
commit 6b2aef0aef
233 changed files with 15122 additions and 1 deletions

913
blog/blog.php Normal file
View File

@ -0,0 +1,913 @@
<?php
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
class blog extends common
{
const VERSION = '8.1';
const REALNAME = 'Blog';
const DELETE = true;
const UPDATE = '0.0';
const DATADIRECTORY = ''; // Contenu localisé inclus par défaut (page.json et module.json)
const EDIT_OWNER = 'owner';
const EDIT_GROUP = 'group';
const EDIT_ALL = 'all';
public static $actions = [
'add' => self::GROUP_EDITOR,
'comment' => self::GROUP_EDITOR,
'commentApprove' => self::GROUP_EDITOR,
'commentDelete' => self::GROUP_EDITOR,
'commentDeleteAll' => self::GROUP_EDITOR,
'config' => self::GROUP_EDITOR,
'option' => self::GROUP_EDITOR,
'delete' => self::GROUP_EDITOR,
'edit' => self::GROUP_EDITOR,
'index' => self::GROUP_VISITOR,
'rss' => self::GROUP_VISITOR
];
public static $articles = [];
// Signature du commentaire
public static $editCommentSignature = '';
public static $comments = [];
public static $nbCommentsApproved = 0;
public static $commentsDelete;
// Signatures des commentaires déjà saisis
public static $commentsSignature = [];
public static $pages;
public static $states = [
false => 'Brouillon',
true => 'Publié'
];
public static $pictureSizes = [
'20' => 'Très petite',
'30' => 'Petite',
'40' => 'Grande',
'50' => 'Très Grande',
'100' => 'Pleine largeur',
];
public static $picturePositions = [
'left' => 'À gauche',
'right' => 'À droite ',
];
// Nombre d'objets par page
public static $ArticlesListed = [
1 => '1 article',
2 => '2 articles',
4 => '4 articles',
6 => '6 articles',
8 => '8 articles',
10 => '10 articles',
12 => '12 articles'
];
//Paramètre longueur maximale des commentaires en nb de caractères
public static $commentsLength = [
100 => '100 signes',
250 => '250 signes',
500 => '500 signes',
750 => '750 signes'
];
public static $articlesLenght = [
0 => 'Articles complets',
600 => '600 signes',
800 => '800 signes',
1000 => '1000 signes',
1200 => '1200 signes',
1400 => '1400 signes',
1600 => '1600 signes',
1800 => '1800 signes',
];
public static $articlesLayout = [
false => 'Classique',
true => 'Moderne',
];
// Permissions d'un article
public static $articleConsent = [
self::EDIT_ALL => 'Tous les groupes',
self::EDIT_GROUP => 'Groupe du propriétaire',
self::EDIT_OWNER => 'Propriétaire'
];
public static $dateFormats = [
'%d %B %Y' => 'DD MMMM YYYY',
'%d/%m/%Y' => 'DD/MM/YYYY',
'%m/%d/%Y' => 'MM/DD/YYYY',
'%d/%m/%y' => 'DD/MM/YY',
'%m/%d/%y' => 'MM/DD/YY',
'%d-%m-%Y' => 'DD-MM-YYYY',
'%m-%d-%Y' => 'MM-DD-YYYY',
'%d-%m-%y' => 'DD-MM-YY',
'%m-%d-%y' => 'MM-DD-YY',
];
public static $timeFormats = [
'%H:%M' => 'HH:MM',
'%I:%M %p' => "HH:MM tt",
];
public static $timeFormat = '';
public static $dateFormat = '';
// Nombre d'articles dans la page de config:
public static $itemsperPage = 8;
public static $users = [];
/**
* Mise à jour du module
* Appelée par les fonctions index et config
*/
private function update()
{
// Initialisation
if (is_null($this->getData(['module', $this->getUrl(0), 'config', 'versionData']))) {
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '0.0']);
}
// Version 5.0
if (version_compare($this->getData(['module', $this->getUrl(0), 'config', 'versionData']), '5.0', '<')) {
$this->setData(['module', $this->getUrl(0), 'config', 'itemsperPage', 6]);
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '5.0']);
}
// Version 6.0
if (version_compare($this->getData(['module', $this->getUrl(0), 'config', 'versionData']), '6.0', '<')) {
$this->setData(['module', $this->getUrl(0), 'config', 'feeds', false]);
$this->setData(['module', $this->getUrl(0), 'config', 'feedsLabel', '']);
$this->setData(['module', $this->getUrl(0), 'config', 'articlesLenght', 0]);
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '6.0']);
}
// Version 6.5
if (version_compare($this->getData(['module', $this->getUrl(0), 'config', 'versionData']), '6.5', '<')) {
$this->setData(['module', $this->getUrl(0), 'config', 'dateFormat', '%d %B %Y']);
$this->setData(['module', $this->getUrl(0), 'config', 'timeFormat', '%H:%M']);
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '6.5']);
}
// Version 8.0
if (version_compare($this->getData(['module', $this->getUrl(0), 'config', 'versionData']), '8.0', '<')) {
$this->setData(['module', $this->getUrl(0), 'config', 'buttonBack', true]);
$this->setData(['module', $this->getUrl(0), 'config', 'showTime', true]);
$this->setData(['module', $this->getUrl(0), 'config', 'showDate', true]);
$this->setData(['module', $this->getUrl(0), 'config', 'showPseudo', true]);
$this->setData(['module', $this->getUrl(0), 'config', 'versionData', '8.0']);
}
}
/**
* Flux RSS
*/
public function rss()
{
// Inclure les classes
include_once 'module/blog/vendor/FeedWriter/Item.php';
include_once 'module/blog/vendor/FeedWriter/Feed.php';
include_once 'module/blog/vendor/FeedWriter/RSS2.php';
include_once 'module/blog/vendor/FeedWriter/InvalidOperationException.php';
date_default_timezone_set('UTC');
$feeds = new \FeedWriter\RSS2();
// En-tête
$feeds->setTitle($this->getData(['page', $this->getUrl(0), 'title']) ? $this->getData(['page', $this->getUrl(0), 'title']) : '');
$feeds->setLink(helper::baseUrl() . $this->getUrl(0));
if ($this->getData(['page', $this->getUrl(0), 'metaDescription'])) {
$feeds->setDescription($this->getData(['page', $this->getUrl(0), 'metaDescription']));
}
$feeds->setChannelElement('language', 'fr-FR');
$feeds->setDate(date('r', time()));
$feeds->addGenerator();
// Corps des articles
$articleIdsPublishedOns = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'publishedOn', 'SORT_DESC');
$articleIdsStates = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'state', 'SORT_DESC');
foreach ($articleIdsPublishedOns as $articleId => $articlePublishedOn) {
if ($articlePublishedOn <= time() and $articleIdsStates[$articleId]) {
// Miniature
$parts = pathinfo($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picture']));
$thumb = 'mini_' . $parts['basename'];
// Créer les articles du flux
$newsArticle = $feeds->createNewItem();
// Signature de l'article
$author = $this->signature($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'userId']));
$newsArticle->addElementArray([
'title' => $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'title']),
'link' => helper::baseUrl() . $this->getUrl(0) . '/' . $articleId,
'description' => '<img src="' . helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb
. '" alt="' . $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'title'])
. '" title="' . $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'title'])
. '" />' .
$this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'content']),
]);
$newsArticle->setAuthor($author, 'no@mail.com');
$newsArticle->setId(helper::baseUrl() . $this->getUrl(0) . '/' . $articleId);
$newsArticle->setDate(date('r', $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'publishedOn'])));
if (file_exists($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picture']))) {
$imageData = getimagesize(helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb);
$newsArticle->addEnclosure(
helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb,
$imageData[0] * $imageData[1],
$imageData['mime']
);
}
$feeds->addItem($newsArticle);
}
}
// Valeurs en sortie
$this->addOutput([
'display' => self::DISPLAY_RSS,
'content' => $feeds->generateFeed(),
'view' => 'rss'
]);
}
/**
* Édition
*/
public function add()
{
// Soumission du formulaire
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
// Modification de l'userId
if ($this->getUser('group') === self::GROUP_ADMIN) {
$newuserid = $this->getInput('blogAddUserId', helper::FILTER_STRING_SHORT, true);
} else {
$newuserid = $this->getUser('id');
}
// Incrémente l'id de l'article
$articleId = helper::increment($this->getInput('blogAddPermalink'), $this->getData(['page']));
$articleId = helper::increment($articleId, (array) $this->getData(['module', $this->getUrl(0)]));
$articleId = helper::increment($articleId, array_keys(self::$actions));
// Crée l'article
$this->setData([
'module',
$this->getUrl(0),
'posts',
$articleId,
[
'content' => $this->getInput('blogAddContent', null),
'picture' => $this->getInput('blogAddPicture', helper::FILTER_STRING_SHORT),
'hidePicture' => $this->getInput('blogAddHidePicture', helper::FILTER_BOOLEAN),
'pictureSize' => $this->getInput('blogAddPictureSize', helper::FILTER_STRING_SHORT),
'picturePosition' => $this->getInput('blogAddPicturePosition', helper::FILTER_STRING_SHORT),
'publishedOn' => $this->getInput('blogAddPublishedOn', helper::FILTER_DATETIME, true),
'state' => $this->getInput('blogAddState', helper::FILTER_BOOLEAN),
'title' => $this->getInput('blogAddTitle', helper::FILTER_STRING_SHORT, true),
'userId' => $newuserid,
'editConsent' => $this->getInput('blogAddConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('blogAddConsent'),
'commentMaxlength' => $this->getInput('blogAddCommentMaxlength'),
'commentApproved' => $this->getInput('blogAddCommentApproved', helper::FILTER_BOOLEAN),
'commentClose' => $this->getInput('blogAddCommentClose', helper::FILTER_BOOLEAN),
'commentNotification' => $this->getInput('blogAddCommentNotification', helper::FILTER_BOOLEAN),
'commentGroupNotification' => $this->getInput('blogAddCommentGroupNotification', helper::FILTER_INT),
'comment' => []
]
]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config',
'notification' => helper::translate('Nouvel article créé'),
'state' => true
]);
}
// Liste des utilisateurs
self::$users = helper::arrayColumn($this->getData(['user']), 'firstname');
ksort(self::$users);
foreach (self::$users as $userId => &$userFirstname) {
$userFirstname = $userFirstname . ' ' . $this->getData(['user', $userId, 'lastname']);
}
unset($userFirstname);
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Rédiger un article'),
'vendor' => [
'tinymce',
'furl'
],
'view' => 'add'
]);
}
/**
* Liste des commentaires
*/
public function comment()
{
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
} else {
$comments = $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment']);
self::$commentsDelete = template::button('blogCommentDeleteAll', [
'class' => 'blogCommentDeleteAll buttonRed',
'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDeleteAll/' . $this->getUrl(2),
'value' => 'Tout effacer'
]);
// Ids des commentaires par ordre de création
$commentIds = array_keys(helper::arrayColumn($comments, 'createdOn', 'SORT_DESC'));
// Pagination
$pagination = helper::pagination($commentIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0), 'config', 'itemsperPage']));
// Liste des pages
self::$pages = $pagination['pages'];
// Commentaires en fonction de la pagination
for ($i = $pagination['first']; $i < $pagination['last']; $i++) {
// Met en forme le tableau
$comment = $comments[$commentIds[$i]];
// Bouton d'approbation
$buttonApproval = '';
// Compatibilité avec les commentaires des versions précédentes, les valider
$comment['approval'] = array_key_exists('approval', $comment) === false ? true : $comment['approval'];
if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'commentApproved']) === true) {
$buttonApproval = template::button('blogCommentApproved' . $commentIds[$i], [
'class' => $comment['approval'] === true ? 'blogCommentRejected buttonGreen' : 'blogCommentApproved buttonRed',
'href' => helper::baseUrl() . $this->getUrl(0) . '/commentApprove/' . $this->getUrl(2) . '/' . $commentIds[$i],
'value' => $comment['approval'] === true ? 'A' : 'R',
'help' => $comment['approval'] === true ? 'Approuvé' : 'Rejeté',
]);
}
self::$dateFormat = $this->getData(['module', $this->getUrl(0), 'config', 'dateFormat']);
self::$timeFormat = $this->getData(['module', $this->getUrl(0), 'config', 'timeFormat']);
self::$comments[] = [
helper::dateUTF8(self::$dateFormat, $comment['createdOn'], self::$i18nUI) . ' - ' . helper::dateUTF8(self::$timeFormat, $comment['createdOn'], self::$i18nUI),
$comment['content'],
$comment['userId'] ? $this->getData(['user', $comment['userId'], 'firstname']) . ' ' . $this->getData(['user', $comment['userId'], 'lastname']) : $comment['author'],
$buttonApproval,
template::button('blogCommentDelete' . $commentIds[$i], [
'class' => 'blogCommentDelete buttonRed',
'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDelete/' . $this->getUrl(2) . '/' . $commentIds[$i],
'value' => template::ico('trash')
])
];
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Gestion des commentaires'),
'view' => 'comment'
]);
}
}
/**
* Suppression de commentaire
*/
public function commentDelete()
{
// Le commentaire n'existe pas
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
$this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Suppression
else {
$this->deleteData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3)]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment/' . $this->getUrl(2),
'notification' => helper::translate('Commentaire supprimé'),
'state' => true
]);
}
}
/**
* Suppression de tous les commentairess de l'article $this->getUrl(2)
*/
public function commentDeleteAll()
{
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Suppression
else {
$this->setData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', []]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment',
'notification' => helper::translate('Commentaires supprimés'),
'state' => true
]);
}
}
/**
* Approbation oou désapprobation de commentaire
*/
public function commentApprove()
{
// Le commentaire n'existe pas
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
$this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Inversion du statut
else {
$approved = !$this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3), 'approval']);
$this->setData([
'module',
$this->getUrl(0),
'posts',
$this->getUrl(2),
'comment',
$this->getUrl(3),
[
'author' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3), 'author']),
'content' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3), 'content']),
'createdOn' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3), 'createdOn']),
'userId' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment', $this->getUrl(3), 'userId']),
'approval' => $approved
]
]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment/' . $this->getUrl(2),
'notification' => $approved ? helper::translate('Commentaire approuvé') : helper::translate('Commentaire rejeté'),
'state' => $approved
]);
}
}
/**
* Configuration
*/
public function config()
{
// Mise à jour des données de module
$this->update();
// Ids des articles par ordre de publication
$articleIds = array_keys(helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'publishedOn', 'SORT_DESC'));
// Gestion des droits d'accès
$filterData = [];
foreach ($articleIds as $key => $value) {
if (
( // Propriétaire
$this->getData(['module', $this->getUrl(0), 'posts', $value, 'editConsent']) === self::EDIT_OWNER
and ($this->getData(['module', $this->getUrl(0), 'posts', $value, 'userId']) === $this->getUser('id')
or $this->getUser('group') === self::GROUP_ADMIN)
)
or (
// Groupe
$this->getData(['module', $this->getUrl(0), 'posts', $value, 'editConsent']) !== self::EDIT_OWNER
and $this->getUser('group') >= $this->getData(['module', $this->getUrl(0), 'posts', $value, 'editConsent'])
)
or (
// Tout le monde
$this->getData(['module', $this->getUrl(0), 'posts', $value, 'editConsent']) === self::EDIT_ALL
)
) {
$filterData[] = $value;
}
}
$articleIds = $filterData;
// Pagination
$pagination = helper::pagination($articleIds, $this->getUrl(), self::$itemsperPage);
// Liste des pages
self::$pages = $pagination['pages'];
// Format de temps
self::$dateFormat = $this->getData(['module', $this->getUrl(0), 'config', 'dateFormat']);
self::$timeFormat = $this->getData(['module', $this->getUrl(0), 'config', 'timeFormat']);
// Articles en fonction de la pagination
for ($i = $pagination['first']; $i < $pagination['last']; $i++) {
// Nombre de commentaires à approuver et approuvés
$approvals = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'comment']), 'approval', 'SORT_DESC');
if (is_array($approvals)) {
$a = array_values($approvals);
$toApprove = count(array_keys($a, false));
$approved = count(array_keys($a, true));
} else {
$toApprove = 0;
$approved = count($this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'comment']));
}
// Met en forme le tableau
self::$articles[] = [
'<a href="' . helper::baseurl() . $this->getUrl(0) . '/' . $articleIds[$i] . '" target="_blank" >' .
$this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'title']) .
'</a>',
helper::dateUTF8(self::$dateFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'publishedOn']), self::$i18nUI) . ' - ' . helper::dateUTF8(self::$timeFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'publishedOn']), self::$i18nUI),
self::$states[$this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i], 'state'])],
// Bouton pour afficher les commentaires de l'article
template::button('blogConfigComment' . $articleIds[$i], [
'class' => ($toApprove || $approved) > 0 ? '' : 'buttonGrey',
'href' => ($toApprove || $approved) > 0 ? helper::baseUrl() . $this->getUrl(0) . '/comment/' . $articleIds[$i] : '',
'value' => $toApprove > 0 ? $toApprove . '/' . $approved : $approved,
'help' => ($toApprove || $approved) > 0 ? 'Éditer / Approuver un commentaire' : ''
]),
template::button('blogConfigEdit' . $articleIds[$i], [
'href' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $articleIds[$i],
'value' => template::ico('pencil')
]),
template::button('blogConfigDelete' . $articleIds[$i], [
'class' => 'blogConfigDelete buttonRed',
'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $articleIds[$i],
'value' => template::ico('trash')
])
];
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Configuration du module'),
'view' => 'config'
]);
}
public function option()
{
// Soumission du formulaire
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
$this->setData([
'module',
$this->getUrl(0),
'config',
[
'feeds' => $this->getInput('blogOptionShowFeeds', helper::FILTER_BOOLEAN),
'feedsLabel' => $this->getInput('blogOptionFeedslabel', helper::FILTER_STRING_SHORT),
'layout' => $this->getInput('blogOptionArticlesLayout', helper::FILTER_BOOLEAN),
'articlesLenght' => $this->getInput('blogOptionArticlesLayout', helper::FILTER_BOOLEAN) === false ? $this->getInput('blogOptionArticlesLenght', helper::FILTER_INT) : 0,
'itemsperPage' => $this->getInput('blogOptionItemsperPage', helper::FILTER_INT, true),
'dateFormat' => $this->getInput('blogOptionDateFormat'),
'timeFormat' => $this->getInput('blogOptionTimeFormat'),
'buttonBack' => $this->getInput('blogOptionButtonBack', helper::FILTER_BOOLEAN),
'showDate' => $this->getInput('blogOptionShowDate', helper::FILTER_BOOLEAN),
'showTime' => $this->getInput('blogOptionShowTime', helper::FILTER_BOOLEAN),
'showPseudo' => $this->getInput('blogOptionShowPseudo', helper::FILTER_BOOLEAN),
'versionData' => $this->getData(['module', $this->getUrl(0), 'config', 'versionData']),
]
]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config',
'notification' => helper::translate('Modifications enregistrées'),
'state' => true
]);
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Options de configuration'),
'view' => 'option'
]);
}
/**
* Suppression
*/
public function delete()
{
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true ||
$this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2)]) === null
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Suppression
else {
$this->deleteData(['module', $this->getUrl(0), 'posts', $this->getUrl(2)]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config',
'notification' => helper::translate('Article supprimé'),
'state' => true
]);
}
}
/**
* Édition
*/
public function edit()
{
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// L'article n'existe pas
if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2)]) === null) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// L'article existe
else {
// Soumission du formulaire
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
if ($this->getUser('group') === self::GROUP_ADMIN) {
$newuserid = $this->getInput('blogEditUserId', helper::FILTER_STRING_SHORT, true);
} else {
$newuserid = $this->getUser('id');
}
$articleId = $this->getInput('blogEditPermalink', null, true);
// Incrémente le nouvel id de l'article
if ($articleId !== $this->getUrl(2)) {
$articleId = helper::increment($articleId, $this->getData(['page']));
$articleId = helper::increment($articleId, $this->getData(['module', $this->getUrl(0), 'posts']));
$articleId = helper::increment($articleId, array_keys(self::$actions));
}
$this->setData([
'module',
$this->getUrl(0),
'posts',
$articleId,
[
'title' => $this->getInput('blogEditTitle', helper::FILTER_STRING_SHORT, true),
'comment' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'comment']),
'content' => $this->getInput('blogEditContent', null),
'picture' => $this->getInput('blogEditPicture', helper::FILTER_STRING_SHORT),
'hidePicture' => $this->getInput('blogEditHidePicture', helper::FILTER_BOOLEAN),
'pictureSize' => $this->getInput('blogEditPictureSize', helper::FILTER_STRING_SHORT),
'picturePosition' => $this->getInput('blogEditPicturePosition', helper::FILTER_STRING_SHORT),
'publishedOn' => $this->getInput('blogEditPublishedOn', helper::FILTER_DATETIME, true),
'state' => $this->getInput('blogEditState', helper::FILTER_BOOLEAN),
'userId' => $newuserid,
'editConsent' => $this->getInput('blogEditConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('blogEditConsent'),
'commentMaxlength' => $this->getInput('blogEditCommentMaxlength'),
'commentApproved' => $this->getInput('blogEditCommentApproved', helper::FILTER_BOOLEAN),
'commentClose' => $this->getInput('blogEditCommentClose', helper::FILTER_BOOLEAN),
'commentNotification' => $this->getInput('blogEditCommentNotification', helper::FILTER_BOOLEAN),
'commentGroupNotification' => $this->getInput('blogEditCommentGroupNotification', helper::FILTER_INT)
]
]);
// Supprime l'ancien article
if ($articleId !== $this->getUrl(2)) {
$this->deleteData(['module', $this->getUrl(0), 'posts', $this->getUrl(2)]);
}
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config',
'notification' => helper::translate('Modifications enregistrées'),
'state' => true
]);
}
// Liste des utilisateurs
self::$users = helper::arrayColumn($this->getData(['user']), 'firstname');
ksort(self::$users);
foreach (self::$users as $userId => &$userFirstname) {
// Les membres ne sont pas éditeurs, les exclure de la liste
if ($this->getData(['user', $userId, 'group']) < self::GROUP_EDITOR) {
unset(self::$users[$userId]);
}
$userFirstname = $userFirstname . ' ' . $this->getData(['user', $userId, 'lastname']) . ' (' . self::$groupEdits[$this->getData(['user', $userId, 'group'])] . ')';
}
unset($userFirstname);
// Valeurs en sortie
$this->addOutput([
'title' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'title']),
'vendor' => [
'tinymce',
'furl'
],
'view' => 'edit'
]);
}
}
/**
* Accueil (deux affichages en un pour éviter une url à rallonge)
*/
public function index()
{
// Mise à jour des données de module
$this->update();
// Affichage d'un article
if (
$this->getUrl(1)
// Protection pour la pagination, un ID ne peut pas être un entier, une page oui
and intval($this->getUrl(1)) === 0
) {
// L'article n'existe pas
if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1)]) === null) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// L'article existe
else {
// Soumission du formulaire
if (
$this->isPost()
) {
// Check la captcha
if (
$this->isConnected() === false
//AND $this->getInput('blogArticlecaptcha', helper::FILTER_INT) !== $this->getInput('blogArticlecaptchaFirstNumber', helper::FILTER_INT) + $this->getInput('blogArticlecaptchaSecondNumber', helper::FILTER_INT))
and password_verify($this->getInput('blogArticleCaptcha', helper::FILTER_INT), $this->getInput('blogArticleCaptchaResult')) === false
) {
self::$inputNotices['blogArticleCaptcha'] = 'Incorrect';
} else {
// Création du commentaire et notifcation par email
$commentId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'comment']));
$content = $this->getInput('blogArticleContent', null, true);
$this->setData([
'module',
$this->getUrl(0),
'posts',
$this->getUrl(1),
'comment',
$commentId,
[
'author' => $this->getInput('blogArticleAuthor', helper::FILTER_STRING_SHORT, empty($this->getInput('blogArticleUserId')) ? TRUE : FALSE),
'content' => $content,
'createdOn' => time(),
'userId' => $this->getInput('blogArticleUserId'),
'approval' => !$this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentApproved']) // true commentaire publié false en attente de publication
]
]);
// Envoi d'une notification aux administrateurs
// Init tableau
$to = [];
// Liste des destinataires
foreach ($this->getData(['user']) as $userId => $user) {
if ($user['group'] >= $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentGroupNotification'])) {
$to[] = $user['mail'];
$firstname[] = $user['firstname'];
$lastname[] = $user['lastname'];
}
}
// Envoi du mail $sent code d'erreur ou de réussite
$notification = $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentApproved']) === true ? 'Commentaire déposé en attente d\'approbation' : 'Commentaire déposé';
if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentNotification']) === true) {
$error = 0;
foreach ($to as $key => $adress) {
$sent = $this->sendMail(
$adress,
'Nouveau commentaire déposé',
'<p>Bonjour' . ' <strong>' . $firstname[$key] . ' ' . $lastname[$key] . '</strong>,</p>' .
'<p>L\'article <a href="' . helper::baseUrl() . $this->getUrl(0) . '/ ' . $this->getUrl(1) . '">' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'title']) . '</a> a reçu un nouveau commentaire rédigé par <strong>' .
$this->getInput('blogArticleAuthor', helper::FILTER_STRING_SHORT, empty($this->getInput('blogArticleUserId')) ? TRUE : FALSE) . '</strong></p>' .
'<p>' . $content.'</p>',
null,
$this->getData(['config', 'smtp', 'from'])
);
if ($sent === false)
$error++;
}
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl() . '#comment',
'notification' => ($error === 0 ? $notification . '<br/>Une notification a été envoyée.' : $notification . '<br/> Erreur de notification : ' . $sent),
'state' => ($sent === true ? true : null)
]);
} else {
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl() . '#comment',
'notification' => $notification,
'state' => true
]);
}
}
}
// Ids des commentaires approuvés par ordre de publication
$commentsApproved = $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'comment']);
if ($commentsApproved) {
foreach ($commentsApproved as $key => $value) {
if ($value['approval'] === false)
unset($commentsApproved[$key]);
}
// Ligne suivante si affichage du nombre total de commentaires approuvés sous l'article
self::$nbCommentsApproved = count($commentsApproved);
}
$commentIds = array_keys(helper::arrayColumn($commentsApproved, 'createdOn', 'SORT_DESC'));
// Pagination
$pagination = helper::pagination($commentIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0), 'config', 'itemsperPage']), '#comment');
// Liste des pages
self::$pages = $pagination['pages'];
// Signature du commentaire édité
if ($this->isConnected() === true) {
self::$editCommentSignature = $this->signature($this->getUser('id'));
}
// Commentaires en fonction de la pagination
for ($i = $pagination['first']; $i < $pagination['last']; $i++) {
// Signatures des commentaires
$e = $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'comment', $commentIds[$i], 'userId']);
if ($e) {
self::$commentsSignature[$commentIds[$i]] = $this->signature($e);
} else {
self::$commentsSignature[$commentIds[$i]] = $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'comment', $commentIds[$i], 'author']);
}
// Données du commentaire si approuvé
if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'comment', $commentIds[$i], 'approval']) === true) {
self::$comments[$commentIds[$i]] = $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'comment', $commentIds[$i]]);
}
}
// Format de temps
self::$dateFormat = $this->getData(['module', $this->getUrl(0), 'config', 'dateFormat']);
self::$timeFormat = $this->getData(['module', $this->getUrl(0), 'config', 'timeFormat']);
// Valeurs en sortie
$this->addOutput([
'showBarEditButton' => true,
'title' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'title']),
'vendor' => [
'tinymce'
],
'view' => 'article'
]);
}
}
// Liste des articles
else {
// Ids des articles par ordre de publication
$articleIdsPublishedOns = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'publishedOn', 'SORT_DESC');
$articleIdsStates = helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'posts']), 'state', 'SORT_DESC');
$articleIds = [];
foreach ($articleIdsPublishedOns as $articleId => $articlePublishedOn) {
if ($articlePublishedOn <= time() and $articleIdsStates[$articleId]) {
$articleIds[] = $articleId;
// Nombre de commentaires approuvés par article
self::$comments[$articleId] = 0;
if (is_array($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'comment']))) {
foreach ($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'comment']) as $commentId => $commentValue) {
if ($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'comment', $commentId, 'approval'])) {
self::$comments[$articleId] = self::$comments[$articleId] + 1;
}
}
}
}
}
// Pagination
$pagination = helper::pagination($articleIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0), 'config', 'itemsperPage']), '#article');
// Liste des pages
self::$pages = $pagination['pages'];
// Articles en fonction de la pagination
for ($i = $pagination['first']; $i < $pagination['last']; $i++) {
self::$articles[$articleIds[$i]] = $this->getData(['module', $this->getUrl(0), 'posts', $articleIds[$i]]);
}
// Format de temps
self::$dateFormat = $this->getData(['module', $this->getUrl(0), 'config', 'dateFormat']);
self::$timeFormat = $this->getData(['module', $this->getUrl(0), 'config', 'timeFormat']);
// Valeurs en sortie
$this->addOutput([
'showBarEditButton' => true,
'showPageContent' => true,
'view' => 'index'
]);
}
}
}

64
blog/changes.md Normal file
View File

@ -0,0 +1,64 @@
# Version 8.01
- Un mail de notification est seulement envoyé lorsque le captcha est passé et que le commentaire est déposé.
- Le mail de notification contient désormais le nom de l'auteur du commentaire ainsi que le commentaire.
# Version 8.00
- Ajoute trois nouvelles options pour afficher ou masquer le pseudo, la date et l'heure de l'article.
- Corrige un bug d'affichage des articles lorsque le thème Moderne est sélectionné.
- Corrige un bug dans la méthode de tronquage de l'article, nécessite Zwii 13.5
- Corrige un mauvais format de la propriété buttonBack non stockée au type booléen.
# Version 7.11
- Le sélecteur de fichier affiche par défaut le chemin vers le fichier présent dans le champ.
# Version 7.10
- Empêche la validation d'un commentaire lorsque le contenu est vide.
# Versions 7.8 - 7.9
- Le flux RSS ne fonctionne pas si les méta de la page sont vides.
# Version 7.7
- Contrôle de la variable de session liée au contenu. Evite des erreurs lorsque plusieurs onglets sont ouverts.
# Version 7.6
- Mise à jour RSS Feed
# Version 7.5
- Bug paramètre de localisation erroné
# Version 7.4
- Bouton de retour dans les articles
# Version 7.3
- Termes des commandes de profils
# Version 7.2
- Problème avec la portée de la fonction signature déplacée
# Version 7.1
- Permission lors de la validation d'un formulaire
# Version 7.0
- Gestion des permissions intégrée dans le module
# version 6.9
- Bloque l'effacement de l'article selon le profil
# version 6.8
- Masque le code de vérification
- Corrige un défaut d'affichage de l'article dans l'index, lorsque l'article ne contient pas d'image.
- Erreur de décompte des commentaires approuvés ou non dans l'index
# version 6.7
- Ancre article qui affiche le début de l'article au clic sur la barre de pages, fonction utile lorsque le module est sous la page
# version 6.6
- Position de l'icône RSS
- Présentation en tableau amélioration du visuel
# version 6.5
- Intl dates formats
- Modifications liées à la suppression de flatpickr
# version 6.4
- Multilinguisme
# version 6.31
- Miniature de l'image dans le flux RSS
# version 6.3
- Nettoyage de fonction inutile
- Initialisation
# version 6.22
- mise en oeuvre du helper dateUTF8
- Réglage de l'initialisation de la configuration
# version 6.2
- date des commentaires
# version 6.1
- Icône RSS, lien incorrect dans l'article.
# version 6
- mise à la norme avec le module news : le formulaire est sorti de l'écran principal
- Présentation moderner de la liste des articles
- Respect des balises headings
- permalink des articles
- Image de couverture non obligatoire, présentation adaptative de la liste classique des articles.

1
blog/enum.json Normal file
View File

@ -0,0 +1 @@
{"name":"blog","realName":"Blog","version":"8.1","update":"0.0","delete":true,"dataDirectory":""}

57
blog/i18n/de.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "1 Artikel",
"10 articles": "10 Artikel",
"100 signes": "100 Zeichen",
"12 articles": "12 Artikel",
"2 articles": "2 Artikel",
"250 signes": "250 Zeichen",
"4 articles": "4 Artikel",
"500 signes": "500 Zeichen",
"6 articles": "6 Artikel",
"750 signes": "750 Zeichen",
"8 articles": "8 Artikel",
"Approbation par un modérateur": "Genehmigung durch einen Moderator",
"Approuver le commentaire ?": "Den Kommentar genehmigen?",
"Approuvé": "Genehmigt",
"Article supprimé": "Artikel gelöscht",
"Articles par page": "Artikel pro Seite",
"Aucun article": "Kein Artikel",
"Aucun commentaire": "Kein Kommentar",
"Auteur": "Auteur",
"Brouillon": "Unorganisiert",
"Caractères par commentaire": "Charaktere nach Kommentar",
"Commentaire approuvé": "Genehmigter Kommentar",
"Commentaire rejeté": "Abgelehnter Kommentar",
"Commentaire supprimé": "Kommentar gelöscht",
"Commentaires": "Kommentare",
"Commentaires supprimés": "Gelöschte Kommentare",
"Fermer les commentaires": "Schalten Sie die Kommentare aus",
"Gestion des commentaires": "Kommentarmanagement",
"Groupe du propriétaire": "Besitzergruppe",
"Image de couverture": "Berichterstattung",
"Informations générales": "Allgemeine Informationen",
"Lien du flux RSS": "Lien du flux RSS",
"Lire la suite": "Mehr lesen",
"Notification par email": "Benachrichtigung PAR -E -Mail",
"Nouvel article créé": "Neuer Artikel erstellt",
"Options de configuration": "Optionen de Konfiguration",
"Options de publication": "Publikationsoptionen",
"Permalink": "Permalink",
"Pleine largeur": "Gesamtbreite",
"Propriétaire": "Eigentümer",
"Publication": "Veröffentlichung",
"Publier": "Veröffentlichen",
"Rejeter le commentaire ?": "Den Kommentar ablehnen?",
"Rejeté": "Abgelehnt",
"Rédiger un article": "Artikel",
"Supprimer cet article ?": "Diesen Artikel löschen?",
"Supprimer le commentaire ?": "Den Kommentar löschen?",
"Tous les groupes": "Alle Gruppen",
"Tout effacer": "Alles löschen",
"Très Grande": "Sehr groß",
"État": "État",
"Ajouter un article": "Einen Artikel hinzufügen",
"Éditer un article!": "Einen Artikel bearbeiten",
"Effacer un article": "Einen Artikel löschen",
"Options des articles": "Artikelmöglichkeiten"
}

57
blog/i18n/en_EN.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "1 article",
"10 articles": "10 articles",
"100 signes": "100 signs",
"12 articles": "12 articles",
"2 articles": "2 articles",
"250 signes": "250 signs",
"4 articles": "4 articles",
"500 signes": "500 signs",
"6 articles": "6 articles",
"750 signes": "750 signs",
"8 articles": "8 articles",
"Approbation par un modérateur": "Approval by a moderator",
"Approuver le commentaire ?": "Approve the comment?",
"Approuvé": "Approved",
"Article supprimé": "Deleted article",
"Articles par page": "Articles per page",
"Aucun article": "No article",
"Aucun commentaire": "No comment",
"Auteur": "Auteur",
"Brouillon": "Draft copy",
"Caractères par commentaire": "Characters by comment",
"Commentaire approuvé": "Approved comment",
"Commentaire rejeté": "Rejected commentary",
"Commentaire supprimé": "Deleted comment",
"Commentaires": "Comments",
"Commentaires supprimés": "Deleted comments",
"Fermer les commentaires": "Turn off the comments",
"Gestion des commentaires": "Comment management",
"Groupe du propriétaire": "Owner's group",
"Image de couverture": "Coverage",
"Informations générales": "General informations",
"Lien du flux RSS": "Lien du Flux RSS",
"Lire la suite": "Read more",
"Notification par email": "Notification par email",
"Nouvel article créé": "New article created",
"Options de configuration": "Configuration options",
"Options de publication": "Publication options",
"Permalink": "Permalink",
"Pleine largeur": "Full width",
"Propriétaire": "Owner",
"Publication": "Publication",
"Publier": "Publish",
"Rejeter le commentaire ?": "Reject the comment?",
"Rejeté": "Rejected",
"Rédiger un article": "Write an article",
"Supprimer cet article ?": "Delete this article?",
"Supprimer le commentaire ?": "Delete the comment?",
"Tous les groupes": "All groups",
"Tout effacer": "Erase everything",
"Très Grande": "Very tall",
"État": "Status",
"Ajouter un article": "Add an article",
"Éditer un article!": "Edit an article",
"Effacer un article": "Delete an article",
"Options des articles": "Article options"
}

57
blog/i18n/es.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "1 Artículo",
"10 articles": "10 Artículos",
"100 signes": "100 caracteres",
"12 articles": "12 Artículos",
"2 articles": "2 Artículos",
"250 signes": "250 caracteres",
"4 articles": "4 Artículos",
"500 signes": "500 caracteres",
"6 articles": "6 Artículos",
"750 signes": "750 caracteres",
"8 articles": "8 Artículos",
"Approbation par un modérateur": "Aprobación del moderador",
"Approuver le commentaire ?": "¿Aprobar el comentario?",
"Approuvé": "Aprobado",
"Article supprimé": "Artículo suprimido",
"Articles par page": "Artículos por página",
"Aucun article": "Ningún artículo",
"Aucun commentaire": "Sin comentarios",
"Auteur": "Autor",
"Brouillon": "Borrador",
"Caractères par commentaire": "Caracteres por comentario",
"Commentaire approuvé": "Comentario aprobado",
"Commentaire rejeté": "Comentario rechazado",
"Commentaire supprimé": "Comentario eliminado",
"Commentaires": "Comentarios",
"Commentaires supprimés": "Comentarios eliminados",
"Fermer les commentaires": "Cerrar los comentarios",
"Gestion des commentaires": "Gestión de comentarios",
"Groupe du propriétaire": "Grupo de propietarios",
"Image de couverture": "Imagen de portada",
"Informations générales": "Información general",
"Lien du flux RSS": "Enlace de fuente RSS",
"Lire la suite": "Leer más",
"Notification par email": "Notificación por correo electrónico",
"Nouvel article créé": "Nuevo artículo creado",
"Options de configuration": "Opciones de configuración",
"Options de publication": "Opciones de publicación",
"Permalink": "Enlace permanente",
"Pleine largeur": "Anchura completa",
"Propriétaire": "Propietario",
"Publication": "Publicación",
"Publier": "Publicar",
"Rejeter le commentaire ?": "¿Rechazar el comentario?",
"Rejeté": "Rechazado",
"Rédiger un article": "Escribir un artículo",
"Supprimer cet article ?": "¿Borrar este artículo?",
"Supprimer le commentaire ?": "¿Borrar el comentario?",
"Tous les groupes": "Todos los grupos",
"Tout effacer": "Borrar todo",
"Très Grande": "Muy grande",
"État": "Estado",
"Ajouter un article": "Agregar un artículo",
"Éditer un article!": "Editar un artículo",
"Effacer un article": "Borrar un artículo",
"Options des articles": "Opciones de artículos"
}

57
blog/i18n/fr_FR.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "",
"10 articles": "",
"100 signes": "",
"12 articles": "",
"2 articles": "",
"250 signes": "",
"4 articles": "",
"500 signes": "",
"6 articles": "",
"750 signes": "",
"8 articles": "",
"Approbation par un modérateur": "",
"Approuver le commentaire ?": "",
"Approuvé": "",
"Article supprimé": "",
"Articles par page": "",
"Aucun article": "",
"Aucun commentaire": "",
"Auteur": "",
"Brouillon": "",
"Caractères par commentaire": "",
"Commentaire approuvé": "",
"Commentaire rejeté": "",
"Commentaire supprimé": "",
"Commentaires": "",
"Commentaires supprimés": "",
"Fermer les commentaires": "",
"Gestion des commentaires": "",
"Groupe du propriétaire": "",
"Image de couverture": "",
"Informations générales": "",
"Lien du flux RSS": "",
"Lire la suite": "",
"Notification par email": "",
"Nouvel article créé": "",
"Options de configuration": "",
"Options de publication": "",
"Permalink": "",
"Pleine largeur": "",
"Propriétaire": "",
"Publication": "",
"Publier": "",
"Rejeter le commentaire ?": "",
"Rejeté": "",
"Rédiger un article": "",
"Supprimer cet article ?": "",
"Supprimer le commentaire ?": "",
"Tous les groupes": "",
"Tout effacer": "",
"Très Grande": "",
"État": "",
"Ajouter un article": "",
"Éditer un article!": "",
"Effacer un article": "",
"Options des articles": ""
}

57
blog/i18n/gr_GR.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "1 Άρθρο",
"10 articles": "10 Άρθρα",
"100 signes": "100 χαρακτήρες",
"12 articles": "12 Άρθρα",
"2 articles": "2 Άρθρα",
"250 signes": "250 χαρακτήρες",
"4 articles": "4 Άρθρα",
"500 signes": "500 χαρακτήρες",
"6 articles": "6 Άρθρα",
"750 signes": "750 χαρακτήρες",
"8 articles": "8 Άρθρα",
"Approbation par un modérateur": "Έγκριση επόπτη",
"Approuver le commentaire ?": "Εγκρίνετε το σχόλιο;",
"Approuvé": "Εγκεκριμένο",
"Article supprimé": "Άρθρο που διαγράφηκε",
"Articles par page": "Άρθρα ανά σελίδα",
"Aucun article": "κανένα άρθρο",
"Aucun commentaire": "Κανένα σχόλιο",
"Auteur": "Συγγραφέας",
"Brouillon": "Σχέδιο",
"Caractères par commentaire": "Χαρακτήρες ανά σχόλιο",
"Commentaire approuvé": "Σχόλιο εγκεκριμένο",
"Commentaire rejeté": "Σχόλιο απορρίφθηκε",
"Commentaire supprimé": "Σχόλιο διαγράφηκε",
"Commentaires": "Σχόλια",
"Commentaires supprimés": "Σχόλια διαγράφονται",
"Fermer les commentaires": "Κλείσιμο σχολίων",
"Gestion des commentaires": "Διαχείριση σχολίων",
"Groupe du propriétaire": "Ομάδα ιδιοκτήτη",
"Image de couverture": "εικόνα εξωφύλλου",
"Informations générales": "Γενικές πληροφορίες",
"Lien du flux RSS": "Σύνδεσμος τροφοδοσίας RSS",
"Lire la suite": "Διαβάστε περισσότερα",
"Notification par email": "Ειδοποίηση ηλεκτρονικού ταχυδρομείου",
"Nouvel article créé": "Δημιουργήθηκε νέο άρθρο",
"Options de configuration": "Opções de configuração",
"Options de publication": "Επιλογές δημοσίευσης",
"Permalink": "Μόνιμος σύνδεσμος",
"Pleine largeur": "Πλήρες πλάτος",
"Propriétaire": "Ιδιοκτήτης",
"Publication": "ημερομηνία δημοσίευσης",
"Publier": "δημοσιεύστε το άρθρο",
"Rejeter le commentaire ?": "Απορρίπτετε το σχόλιο;",
"Rejeté": "Απορρίφθηκε",
"Rédiger un article": "Γράψτε ένα άρθρο",
"Supprimer cet article ?": "Να διαγράψετε αυτό το άρθρο;",
"Supprimer le commentaire ?": "Διαγράψτε το σχόλιο;",
"Tous les groupes": "Όλες οι ομάδες",
"Tout effacer": "Διαγραφή όλων",
"Très Grande": "Πολύ μεγάλη",
"État": "κατάσταση",
"Ajouter un article": "Προσθήκη άρθρου",
"Éditer un article!": "Επεξεργασία άρθρου",
"Effacer un article": "Διαγραφή άρθρου",
"Options des articles": "Επιλογές άρθρων"
}

57
blog/i18n/it.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "1 articolo",
"10 articles": "10 articoli",
"100 signes": "100 segni",
"12 articles": "12 articoli",
"2 articles": "2 articoli",
"250 signes": "250 segni",
"4 articles": "4 articoli",
"500 signes": "500 segni",
"6 articles": "6 articoli",
"750 signes": "750 segni",
"8 articles": "8 articoli",
"Approbation par un modérateur": "Approvazione da parte di un moderatore",
"Approuver le commentaire ?": "Approvare il commento?",
"Approuvé": "Approvato",
"Article supprimé": "Articolo cancellato",
"Articles par page": "Articoli per pagina",
"Aucun article": "Nessun articolo",
"Aucun commentaire": "Nessun commento",
"Auteur": "Auteur",
"Brouillon": "Progetto di copia",
"Caractères par commentaire": "Personaggi per commento",
"Commentaire approuvé": "Commento approvato",
"Commentaire rejeté": "Commento respinto",
"Commentaire supprimé": "Commento cancellato",
"Commentaires": "Commenti",
"Commentaires supprimés": "Commenti cancellati",
"Fermer les commentaires": "Disattiva i commenti",
"Gestion des commentaires": "Gestione dei commenti",
"Groupe du propriétaire": "Gruppo del proprietario",
"Image de couverture": "Copertura",
"Informations générales": "Informazioni generali",
"Lien du flux RSS": "Lien Du Flux RSS",
"Lire la suite": "Continua a leggere",
"Notification par email": "Email di notifica par",
"Nouvel article créé": "Nuovo articolo creato",
"Options de configuration": "Opzioni di configurazione",
"Options de publication": "Opzioni di pubblicazione",
"Permalink": "Permalink",
"Pleine largeur": "Intera larghezza",
"Propriétaire": "Proprietario",
"Publication": "Pbblicazione",
"Publier": "Pubblicare",
"Rejeter le commentaire ?": "Rifiutare il commento?",
"Rejeté": "Respinto",
"Rédiger un article": "Scrivi un articolo",
"Supprimer cet article ?": "Elimina questo articolo?",
"Supprimer le commentaire ?": "Elimina il commento?",
"Tous les groupes": "Tutti i gruppi",
"Tout effacer": "Cancellare tutto",
"Très Grande": "Molto alto",
"État": "Stato",
"Ajouter un article": "Aggiungi un articolo",
"Éditer un article!": "Modifica un articolo",
"Effacer un article": "Cancella un articolo",
"Options des articles": "Opzioni degli articoli"
}

57
blog/i18n/pt_PT.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "1 artigo",
"10 articles": "10 artigos",
"100 signes": "100 sinais",
"12 articles": "12 artigos",
"2 articles": "2 artigos",
"250 signes": "250 sinais",
"4 articles": "4 artigos",
"500 signes": "500 sinais",
"6 articles": "6 artigos",
"750 signes": "750 sinais",
"8 articles": "8 artigos",
"Approbation par un modérateur": "Aprovação por um moderador",
"Approuver le commentaire ?": "Aprovar o comentário?",
"Approuvé": "Aprovado",
"Article supprimé": "Artigo excluído",
"Articles par page": "Artigos por página",
"Aucun article": "Nenhum artigo",
"Aucun commentaire": "Sem comentários",
"Auteur": "Autora",
"Brouillon": "Cópia rascunho",
"Caractères par commentaire": "Personagens por comentários",
"Commentaire approuvé": "Comentário aprovado",
"Commentaire rejeté": "Comentário rejeitado",
"Commentaire supprimé": "Comentário excluído",
"Commentaires": "Comentários",
"Commentaires supprimés": "Comentários excluídos",
"Fermer les commentaires": "Desligue os comentários",
"Gestion des commentaires": "Gerenciamento de comentários",
"Groupe du propriétaire": "Grupo do proprietário",
"Image de couverture": "Cobertura",
"Informations générales": "Informações gerais",
"Lien du flux RSS": "Lien du Flux RSS",
"Lire la suite": "Leia mais",
"Notification par email": "Notificação por e -mail",
"Nouvel article créé": "Novo artigo criado",
"Options de configuration": "Opções de configuração",
"Options de publication": "Opções de publicação",
"Permalink": "Permalink",
"Pleine largeur": "Largura completa",
"Propriétaire": "Proprietário",
"Publication": "Publicação",
"Publier": "Publicar",
"Rejeter le commentaire ?": "Rejeitar o comentário?",
"Rejeté": "Rejeitado",
"Rédiger un article": "Escrever um artigo",
"Supprimer cet article ?": "Excluir este artigo?",
"Supprimer le commentaire ?": "Excluir o comentário?",
"Tous les groupes": "Todos os grupos",
"Tout effacer": "Apague tudo",
"Très Grande": "Muito alto",
"État": "Estado",
"Ajouter un article": "Adicionar um artigo",
"Éditer un article!": "Editar um artigo",
"Effacer un article": "Apagar um artigo",
"Options des articles": "Opções de artigos"
}

57
blog/i18n/tr_TR.json Normal file
View File

@ -0,0 +1,57 @@
{
"1 article": "1 makale",
"10 articles": "10 makale",
"100 signes": "100 karakter",
"12 articles": "12 makale",
"2 articles": "2 makale",
"250 signes": "250 karakter",
"4 articles": "4 makale",
"500 signes": "500 karakter",
"6 articles": "6 makale",
"750 signes": "750 karakter",
"8 articles": "8 makale",
"Approbation par un modérateur": "Moderatör onayı",
"Approuver le commentaire ?": "Yorum onaylansın mı?",
"Approuvé": "Onaylandı",
"Article supprimé": "Silinen makale",
"Articles par page": "Sayfa başına makale",
"Aucun article": "Makale yok",
"Aucun commentaire": "Yorum yok",
"Auteur": "Yazar",
"Brouillon": "Karalama",
"Caractères par commentaire": "Yorum başına karakter",
"Commentaire approuvé": "Onaylanan yorum",
"Commentaire rejeté": "Reddedilen yorum",
"Commentaire supprimé": "Silinen yorum",
"Commentaires": "Yorumlar",
"Commentaires supprimés": "Silinen yorumlar",
"Fermer les commentaires": "Yorumları kapat",
"Gestion des commentaires": "Yorum yönetimi",
"Groupe du propriétaire": "Sahip grubu",
"Image de couverture": "Kapak resmi",
"Informations générales": "Genel bilgiler",
"Lien du flux RSS": "RSS dağıtım bağlantısı",
"Lire la suite": "Devamını oku",
"Notification par email": "Eposta bildirimi",
"Nouvel article créé": "Yeni makale oluşturuldu",
"Options de configuration": "Yapılandırma seçenekleri",
"Options de publication": "Yayınlama seçenekleri",
"Permalink": "Kalıcı bağlantı",
"Pleine largeur": "Tam genişlik",
"Propriétaire": "Mal sahibi",
"Publication": "Yayın",
"Publier": "Yayınla",
"Rejeter le commentaire ?": "Yorum reddedilsin mi?",
"Rejeté": "Reddedildi",
"Rédiger un article": "Bir makale yaz",
"Supprimer cet article ?": "Bu makale silinsin mi?",
"Supprimer le commentaire ?": "Yorum silinsin mi?",
"Tous les groupes": "Tüm gruplar",
"Tout effacer": "Her şeyi sil",
"Très Grande": "Çok büyük",
"État": "Durum",
"Ajouter un article": "Makale ekleyin",
"Éditer un article!": "Makale düzenleyin",
"Effacer un article": "Makale silin",
"Options des articles": "Makale seçenekleri"
}

View File

@ -0,0 +1,18 @@
<?php $moduleData['blog'] = [
'add' => $this->getInput('profilAddBlogAdd', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilAddBlogEdit', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilAddBlogDelete', helper::FILTER_BOOLEAN),
'option' => $this->getInput('profilAddBlogOption', helper::FILTER_BOOLEAN),
'comment' => $this->getInput('profilAddBlogComment', helper::FILTER_BOOLEAN),
'commentApprove' => $this->getInput('profilAddBlogCommentApprove', helper::FILTER_BOOLEAN),
'commentDelete' => $this->getInput('profilAddBlogCommentDelete', helper::FILTER_BOOLEAN),
'commentDeleteAll' => $this->getInput('profilAddBlogCommentDeleteAll', helper::FILTER_BOOLEAN),
'config' => $this->getInput('profilAddBlogAdd', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddBlogEdit', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddBlogDelete', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddBlogOption', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddBlogComment', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddBlogCommentApprove', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddBlogCommentDelete', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddBlogCommentDeleteAll', helper::FILTER_BOOLEAN)
];

View File

@ -0,0 +1,18 @@
<?php $moduleData['blog'] = [
'add' => $this->getInput('profilEditBlogAdd', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilEditBlogEdit', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilEditBlogDelete', helper::FILTER_BOOLEAN),
'option' => $this->getInput('profilEditBlogOption', helper::FILTER_BOOLEAN),
'comment' => $this->getInput('profilEditBlogComment', helper::FILTER_BOOLEAN),
'commentApprove' => $this->getInput('profilEditBlogCommentApprove', helper::FILTER_BOOLEAN),
'commentDelete' => $this->getInput('profilEditBlogCommentDelete', helper::FILTER_BOOLEAN),
'commentDeleteAll' => $this->getInput('profilEditBlogCommentDeleteAll', helper::FILTER_BOOLEAN),
'config' => $this->getInput('profilEditBlogAdd', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditBlogEdit', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditBlogDelete', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditBlogOption', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditBlogComment', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditBlogCommentApprove', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditBlogCommentDelete', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditBlogCommentDeleteAll', helper::FILTER_BOOLEAN)
];

View File

@ -0,0 +1,39 @@
<div class="row">
<div class="col12">
<div class="block">
<h4>
<?php echo sprintf('%s %s', helper::translate('Permissions'), helper::translate('Blog')); ?>
</h4>
<div class="row">
<div class="col3">
<?php echo template::checkbox('profilAddBlogAdd', true, 'Ajouter un article'); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilAddBlogEdit', true, 'Éditer un article'); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilAddBlogDelete', true, 'Effacer un article'); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilAddBlogOption', true, 'Options des articles'); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('profilAddBlogComment', true, 'Gérer les commentaires'); ?>
</div>
<div class="col6 blogAddCommentOptions">
<?php echo template::checkbox('profilAddBlogCommentApprove', true, 'Approuver un commentaire'); ?>
</div>
</div>
<div class="row">
<div class="col6 blogAddCommentOptions">
<?php echo template::checkbox('profilAddBlogCommentDelete', true, 'Effacer un commentaire'); ?>
</div>
<div class="col6 blogAddCommentOptions">
<?php echo template::checkbox('profilAddBlogCommentDeleteAll', true, 'Effacer tous les commentaires'); ?>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,55 @@
<div class="row">
<div class="col12">
<div class="block">
<h4>
<?php echo sprintf('%s %s', helper::translate('Permissions'), helper::translate('Blog')); ?>
</h4>
<div class="row">
<div class="col3">
<?php echo template::checkbox('profilEditBlogAdd', true, 'Ajouter un article', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'add'])
]); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilEditBlogEdit', true, 'Éditer un article', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'edit'])
]); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilEditBlogDelete', true, 'Effacer un article', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'delete'])
]); ?>
</div>
<div class="col3">
<?php echo template::checkbox('profilEditBlogOption', true, 'Options des articles', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'option'])
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('profilEditBlogComment', true, 'Gérer les commentaires', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'comment'])
]); ?>
</div>
<div class="col6 blogEditCommentOptions">
<?php echo template::checkbox('profilEditBlogCommentApprove', true, 'Approuver un commentaire', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'commentApprove'])
]); ?>
</div>
</div>
<div class="row">
<div class="col6 blogEditCommentOptions">
<?php echo template::checkbox('profilEditBlogCommentDelete', true, 'Effacer un commentaire', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'commentDelete'])
]); ?>
</div>
<div class="col6 blogEditCommentOptions">
<?php echo template::checkbox('profilEditBlogCommentDeleteAll', true, 'Effacer tous les commentaires', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'blog', 'commentDeleteAll'])
]); ?>
</div>
</div>
</div>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

38
blog/vendor/FeedWriter/ATOM.php vendored Normal file
View File

@ -0,0 +1,38 @@
<?php
namespace FeedWriter;
/*
* Copyright (C) 2012 Michael Bemmerl <mail@mx-server.de>
*
* This file is part of the "Universal Feed Writer" project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Wrapper for creating ATOM feeds
*
* @package UniversalFeedWriter
*/
class ATOM extends Feed
{
/**
* {@inheritdoc}
*/
public function __construct()
{
parent::__construct(Feed::ATOM);
}
}

74
blog/vendor/FeedWriter/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,74 @@
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) .
## [v1.1.2] - 2023-05-25
### Changed
- Throw an exception if required feed elements are set with ```NULL``` value. See issue #46.
## [v1.1.1] - 2016-11-19
### Changed
- Improved the documentation.
- Changed to PSR-4 autoloader in composer.json.
### Fixed
- Item::addElement did not method chain in error conditions.
## [v1.1.0] - 2016-11-08
### Added
- Support for multiple element values.
- Support for a feed description in ATOM feeds.
- Support for ATOM feeds without ```link``` elements.
- Support for a feed image in RSS 1.0 and ATOM feeds.
### Changed
- The script does now throw Exceptions instead of stopping the PHP interpreter on error conditions.
- The unique identifier for ATOM feeds / entries use the feed / entry title for generating the ID (previously the feed / entry link).
- Some URI schemes for ```Item::setId``` were wrongly allowed.
- The parameter order of the ```Feed::setImage``` method was changed.
### Fixed
- Fixed slow generation of the feed with huge amounts of feed entries (like 40k entries).
- Fixed PHP warning when ```Feed::setChannelAbout``` for RSS 1.0 feeds was not called.
- A feed element was generated twice if the element content & attribute value was ```NULL```.
- The detection of twice the same link with ```rel=alternate```, ```hreflang``` & ```type``` did not work.
### Removed
- The deprecated method ```Item::setEnclosure``` was removed. Use ```Item::addEnclosure``` instead.
## [v1.0.4] - 2016-04-17
### Changed
- The unique identifier for ATOM feed entries is now compliant to the ATOM standard.
### Fixed
- Filter more invalid XML chars.
- Fixed a PHP warning displayed if ```Feed::setTitle``` or ```Feed::setLink``` was not called.
## [v1.0.3] - 2015-11-11
### Added
- Method for removing tags which were CDATA encoded.
### Fixed
- Fixed error when the filtering of invalid XML chars failed.
- Fixed missing docblock documentation.
## [v1.0.2] - 2015-01-23
### Fixed
- Fixed a wrong docblock return data type.
## [v1.0.1] - 2014-09-21
### Fixed
- Filter invalid XML chars.
## v1.0 - 2014-09-14
[Unreleased]: https://github.com/mibe/FeedWriter/compare/v1.1.2...HEAD
[v1.1.2]: https://github.com/mibe/FeedWriter/compare/v1.1.1...v1.1.2
[v1.1.1]: https://github.com/mibe/FeedWriter/compare/v1.1.0...v1.1.1
[v1.1.0]: https://github.com/mibe/FeedWriter/compare/v1.0.4...v1.1.0
[v1.0.4]: https://github.com/mibe/FeedWriter/compare/v1.0.3...v1.0.4
[v1.0.3]: https://github.com/mibe/FeedWriter/compare/v1.0.2...v1.0.3
[v1.0.2]: https://github.com/mibe/FeedWriter/compare/v1.0.1...v1.0.2
[v1.0.1]: https://github.com/mibe/FeedWriter/compare/v1.0...v1.0.1

1034
blog/vendor/FeedWriter/Feed.php vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
<?php
namespace FeedWriter;
use \LogicException;
/*
* Copyright (C) 2016 Michael Bemmerl <mail@mx-server.de>
*
* This file is part of the "Universal Feed Writer" project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* The exception that is thrown when an invalid operation is performed on
* the object.
*
* @package UniversalFeedWriter
*/
class InvalidOperationException extends LogicException
{
}

415
blog/vendor/FeedWriter/Item.php vendored Normal file
View File

@ -0,0 +1,415 @@
<?php
namespace FeedWriter;
use \DateTime;
use \DateTimeInterface;
/*
* Copyright (C) 2008 Anis uddin Ahmad <anisniit@gmail.com>
* Copyright (C) 2010-2013, 2015-2016 Michael Bemmerl <mail@mx-server.de>
*
* This file is part of the "Universal Feed Writer" project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Universal Feed Writer
*
* Item class - Used as feed element in Feed class
*
* @package UniversalFeedWriter
* @author Anis uddin Ahmad <anisniit@gmail.com>
* @link http://www.ajaxray.com/projects/rss
*/
class Item
{
/**
* Collection of feed item elements
*/
private $elements = array();
/**
* Contains the format of this feed.
*/
private $version;
/**
* Is used as a suffix when multiple elements have the same name.
**/
private $_cpt = 0;
/**
* Constructor
*
* @param string $version constant (RSS1/RSS2/ATOM) RSS2 is default.
*/
public function __construct($version = Feed::RSS2)
{
$this->version = $version;
}
/**
* Return an unique number
*
* @access private
* @return int
**/
private function cpt()
{
return $this->_cpt++;
}
/**
* Add an element to elements array
*
* @access public
* @param string $elementName The tag name of an element
* @param string $content The content of tag
* @param array $attributes Attributes (if any) in 'attrName' => 'attrValue' format
* @param boolean $overwrite Specifies if an already existing element is overwritten.
* @param boolean $allowMultiple Specifies if multiple elements of the same name are allowed.
* @return self
* @throws \InvalidArgumentException if the element name is not a string, empty or NULL.
*/
public function addElement($elementName, $content, array $attributes = null, $overwrite = FALSE, $allowMultiple = FALSE)
{
if (empty($elementName))
throw new \InvalidArgumentException('The element name may not be empty or NULL.');
if (!is_string($elementName))
throw new \InvalidArgumentException('The element name must be a string.');
$key = $elementName;
// return if element already exists & if overwriting is disabled
// & if multiple elements are not allowed.
if (isset($this->elements[$elementName]) && !$overwrite) {
if (!$allowMultiple)
return $this;
$key .= '-' . $this->cpt();
}
$this->elements[$key]['name'] = $elementName;
$this->elements[$key]['content'] = $content;
$this->elements[$key]['attributes'] = $attributes;
return $this;
}
/**
* Set multiple feed elements from an array.
* Elements which have attributes cannot be added by this method
*
* @access public
* @param array array of elements in 'tagName' => 'tagContent' format.
* @return self
*/
public function addElementArray(array $elementArray)
{
foreach ($elementArray as $elementName => $content) {
$this->addElement($elementName, $content);
}
return $this;
}
/**
* Return the collection of elements in this feed item
*
* @access public
* @return array All elements of this item.
* @throws InvalidOperationException on ATOM feeds if either a content or link element is missing.
* @throws InvalidOperationException on RSS1 feeds if a title or link element is missing.
*/
public function getElements()
{
// ATOM feeds have some specific requirements...
if ($this->version == Feed::ATOM)
{
// Add an 'id' element, if it was not added by calling the setLink method.
// Use the value of the title element as key, since no link element was specified.
if (!array_key_exists('id', $this->elements))
$this->setId(Feed::uuid($this->elements['title']['content'], 'urn:uuid:'));
// Either a 'link' or 'content' element is needed.
if (!array_key_exists('content', $this->elements) && !array_key_exists('link', $this->elements))
throw new InvalidOperationException('ATOM feed entries need a link or a content element. Call the setLink or setContent method.');
}
// ...same with RSS1 feeds.
else if ($this->version == Feed::RSS1)
{
if (!array_key_exists('title', $this->elements))
throw new InvalidOperationException('RSS1 feed entries need a title element. Call the setTitle method.');
if (!array_key_exists('link', $this->elements))
throw new InvalidOperationException('RSS1 feed entries need a link element. Call the setLink method.');
}
return $this->elements;
}
/**
* Return the type of this feed item
*
* @access public
* @return string The feed type, as defined in Feed.php
*/
public function getVersion()
{
return $this->version;
}
// Wrapper functions ------------------------------------------------------
/**
* Set the 'description' element of feed item
*
* @access public
* @param string $description The content of the 'description' or 'summary' element
* @return self
*/
public function setDescription($description)
{
$tag = ($this->version == Feed::ATOM) ? 'summary' : 'description';
return $this->addElement($tag, $description);
}
/**
* Set the 'content' element of the feed item
* For ATOM feeds only
*
* @access public
* @param string $content Content for the item (i.e., the body of a blog post).
* @return self
* @throws InvalidOperationException if this method is called on non-ATOM feeds.
*/
public function setContent($content)
{
if ($this->version != Feed::ATOM)
throw new InvalidOperationException('The content element is supported in ATOM feeds only.');
return $this->addElement('content', $content, array('type' => 'html'));
}
/**
* Set the 'title' element of feed item
*
* @access public
* @param string $title The content of 'title' element
* @return self
*/
public function setTitle($title)
{
return $this->addElement('title', $title);
}
/**
* Set the 'date' element of the feed item.
*
* The value of the date parameter can be either a class implementing
* DateTimeInterface, an integer containing a UNIX timestamp or a string
* which is parseable by PHP's 'strtotime' function.
*
* @access public
* @param DateTimeInterface|int|string $date Date which should be used.
* @return self
* @throws \InvalidArgumentException if the given date was not parseable.
*/
public function setDate($date)
{
if (!is_numeric($date)) {
if ($date instanceof DateTimeInterface || $date instanceof DateTime)
$date = $date->getTimestamp();
else {
$date = strtotime($date);
if ($date === FALSE)
throw new \InvalidArgumentException('The given date string was not parseable.');
}
} elseif ($date < 0)
throw new \InvalidArgumentException('The given date is not an UNIX timestamp.');
if ($this->version == Feed::ATOM) {
$tag = 'updated';
$value = date(\DATE_ATOM, $date);
} elseif ($this->version == Feed::RSS2) {
$tag = 'pubDate';
$value = date(\DATE_RSS, $date);
} else {
$tag = 'dc:date';
$value = date("Y-m-d", $date);
}
return $this->addElement($tag, $value);
}
/**
* Set the 'link' element of feed item
*
* @access public
* @param string $link The content of 'link' element
* @return self
*/
public function setLink($link)
{
if ($this->version == Feed::RSS2 || $this->version == Feed::RSS1) {
$this->addElement('link', $link);
} else {
$this->addElement('link','',array('href'=>$link));
$this->setId(Feed::uuid($link,'urn:uuid:'));
}
return $this;
}
/**
* Attach a external media to the feed item.
* Not supported in RSS 1.0 feeds.
*
* See RFC 6838 for syntactical correct MIME types.
*
* Note that you should avoid the use of more than one enclosure in one item,
* since some RSS aggregators don't support it.
*
* @access public
* @param string $url The URL of the media.
* @param integer $length The length of the media.
* @param string $type The MIME type attribute of the media.
* @param boolean $multiple Specifies if multiple enclosures are allowed
* @return self
* @link https://tools.ietf.org/html/rfc6838
* @link http://www.iana.org/assignments/media-types/media-types.xhtml
* @throws \InvalidArgumentException if the length or type parameter is invalid.
* @throws InvalidOperationException if this method is called on RSS1 feeds.
*/
public function addEnclosure($url, $length, $type, $multiple = TRUE)
{
if ($this->version == Feed::RSS1)
throw new InvalidOperationException('Media attachment is not supported in RSS1 feeds.');
// the length parameter should be set to 0 if it can't be determined
// see http://www.rssboard.org/rss-profile#element-channel-item-enclosure
if (!is_numeric($length) || $length < 0)
throw new \InvalidArgumentException('The length parameter must be an integer and greater or equals to zero.');
// Regex used from RFC 4287, page 41
if (!is_string($type) || preg_match('/.+\/.+/', $type) != 1)
throw new \InvalidArgumentException('type parameter must be a string and a MIME type.');
$attributes = array('length' => $length, 'type' => $type);
if ($this->version == Feed::RSS2) {
$attributes['url'] = $url;
$this->addElement('enclosure', '', $attributes, FALSE, $multiple);
} else {
$attributes['href'] = $url;
$attributes['rel'] = 'enclosure';
$this->addElement('atom:link', '', $attributes, FALSE, $multiple);
}
return $this;
}
/**
* Set the 'author' element of feed item.
* Not supported in RSS 1.0 feeds.
*
* @access public
* @param string $author The author of this item
* @param string|null $email Optional email address of the author
* @param string|null $uri Optional URI related to the author
* @return self
* @throws \InvalidArgumentException if the provided email address is syntactically incorrect.
* @throws InvalidOperationException if this method is called on RSS1 feeds.
*/
public function setAuthor($author, $email = null, $uri = null)
{
if ($this->version == Feed::RSS1)
throw new InvalidOperationException('The author element is not supported in RSS1 feeds.');
// Regex from RFC 4287 page 41
if ($email != null && preg_match('/.+@.+/', $email) != 1)
throw new \InvalidArgumentException('The email address is syntactically incorrect.');
if ($this->version == Feed::RSS2)
{
if ($email != null)
$author = $email . ' (' . $author . ')';
$this->addElement('author', $author);
}
else
{
$elements = array('name' => $author);
if ($email != null)
$elements['email'] = $email;
if ($uri != null)
$elements['uri'] = $uri;
$this->addElement('author', $elements);
}
return $this;
}
/**
* Set the unique identifier of the feed item
*
* On ATOM feeds, the identifier must begin with an valid URI scheme.
*
* @access public
* @param string $id The unique identifier of this item
* @param boolean $permaLink The value of the 'isPermaLink' attribute in RSS 2 feeds.
* @return self
* @throws \InvalidArgumentException if the permaLink parameter is not boolean.
* @throws InvalidOperationException if this method is called on RSS1 feeds.
*/
public function setId($id, $permaLink = false)
{
if ($this->version == Feed::RSS2) {
if (!is_bool($permaLink))
throw new \InvalidArgumentException('The permaLink parameter must be boolean.');
$permaLink = $permaLink ? 'true' : 'false';
$this->addElement('guid', $id, array('isPermaLink' => $permaLink));
} elseif ($this->version == Feed::ATOM) {
// Check if the given ID is an valid URI scheme (see RFC 4287 4.2.6)
// The list of valid schemes was generated from http://www.iana.org/assignments/uri-schemes
// by using only permanent or historical schemes.
$validSchemes = array('aaa', 'aaas', 'about', 'acap', 'acct', 'bb', 'cap', 'cid', 'coap', 'coap+tcp', 'coap+ws', 'coaps', 'coaps+tcp', 'coaps+ws', 'crid', 'data', 'dav', 'dict', 'dns', 'drop', 'dtn', 'example', 'fax', 'file', 'filesystem', 'ftp', 'geo', 'go', 'gopher', 'grd', 'h323', 'http', 'https', 'iax', 'icap', 'im', 'imap', 'info', 'ipn', 'ipp', 'ipps', 'iris', 'iris.beep', 'iris.lwz', 'iris.xpc', 'iris.xpcs', 'jabber', 'ldap', 'leaptofrogans', 'mailserver', 'mailto', 'mid', 'modem', 'msrp', 'msrps', 'mt', 'mtqp', 'mupdate', 'news', 'nfs', 'ni', 'nih', 'nntp', 'opaquelocktoken', 'p1', 'pack', 'pkcs11', 'pop', 'pres', 'prospero', 'reload', 'rtsp', 'rtsps', 'rtspu', 'service', 'session', 'shttp (OBSOLETE)', 'sieve', 'sip', 'sips', 'sms', 'snews', 'snmp', 'soap.beep', 'soap.beeps', 'stun', 'stuns', 'tag', 'tel', 'telnet', 'tftp', 'thismessage', 'tip', 'tn3270', 'turn', 'turns', 'tv', 'upt', 'urn', 'vemmi', 'videotex', 'vnc', 'wais', 'wpid', 'ws', 'wss', 'xcon', 'xcon-userid', 'xmlrpc.beep', 'xmlrpc.beeps', 'xmpp', 'z39.50', 'z39.50r', 'z39.50s');
$found = FALSE;
$checkId = strtolower($id);
foreach($validSchemes as $scheme)
if (strrpos($checkId, $scheme . ':', -strlen($checkId)) !== FALSE)
{
$found = TRUE;
break;
}
if (!$found)
throw new \InvalidArgumentException("The ID must begin with an IANA-registered URI scheme.");
$this->addElement('id', $id, NULL, TRUE);
} else
throw new InvalidOperationException('A unique ID is not supported in RSS1 feeds.');
return $this;
}
} // end of class Item

50
blog/vendor/FeedWriter/README.md vendored Normal file
View File

@ -0,0 +1,50 @@
# Generate **RSS 1.0**, **RSS 2.0** or **ATOM** Formatted Feeds
This package can be used to generate feeds in either **RSS 1.0**, **RSS 2.0** or **ATOM** format.
Applications can create a feed object, several feed item objects, set several types of properties of either feed and feed items, and add items to the feed.
Once a feed is fully composed with its items, the feed class can generate the necessary XML structure to describe the feed in **RSS** or **ATOM** format. This structure can be directly sent to the browser, or just returned as string.
## Requirements
- PHP 5.3 or higher
If you don't have **PHP 5.3** available on your system there is a version supporting **PHP 5.0** and above. See the `legacy-php-5.0` branch.
## Installation
You can install via [Composer](https://getcomposer.org/).
composer require mibe/feedwriter
## Documentation
The documentation can be found in the `gh-pages` branch, or on [GitHub Pages](https://mibe.github.io/FeedWriter/).
See the `/examples` directory for usage examples.
See the `CHANGELOG.md` file for changes between the different versions.
## Authors
In chronological order:
- [Anis uddin Ahmad](https://github.com/ajaxray)
- [Michael Bemmerl](https://github.com/mibe)
- Phil Freo
- Paul Ferrett
- Brennen Bearnes
- Michael Robinson
- Baptiste Fontaine
- Kristián Valentín
- Brandtley McMinn
- Julian Bogdani
- Cedric Gampert
- Yamek
- Thielj
- Pavel Khakhlou
- Daniel
- Tino Goratsch
- John
- Özgür Görgülü
- Jan Tojnar

37
blog/vendor/FeedWriter/RSS1.php vendored Normal file
View File

@ -0,0 +1,37 @@
<?php
namespace FeedWriter;
/*
* Copyright (C) 2012 Michael Bemmerl <mail@mx-server.de>
*
* This file is part of the "Universal Feed Writer" project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Wrapper for creating RSS1 feeds
*
* @package UniversalFeedWriter
*/
class RSS1 extends Feed
{
/**
* {@inheritdoc}
*/
public function __construct()
{
parent::__construct(Feed::RSS1);
}
}

37
blog/vendor/FeedWriter/RSS2.php vendored Normal file
View File

@ -0,0 +1,37 @@
<?php
namespace FeedWriter;
/*
* Copyright (C) 2012 Michael Bemmerl <mail@mx-server.de>
*
* This file is part of the "Universal Feed Writer" project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Wrapper for creating RSS2 feeds
*
* @package UniversalFeedWriter
*/
class RSS2 extends Feed
{
/**
* {@inheritdoc}
*/
public function __construct()
{
parent::__construct(Feed::RSS2);
}
}

21
blog/vendor/furl/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Vedat Taylan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

4
blog/vendor/furl/README.md vendored Normal file
View File

@ -0,0 +1,4 @@
# furl
jQuery Furl (Friendly URL) Plugin 1.0.0
Usage : $('#InputID').furl({id:'ReplaceInputID', seperate (optional) : '_' });

3
blog/vendor/furl/inc.json vendored Normal file
View File

@ -0,0 +1,3 @@
[
"jquery.furl.js"
]

76
blog/vendor/furl/jquery.furl.js vendored Normal file
View File

@ -0,0 +1,76 @@
/*
* jQuery Furl (Friendly URL) Plugin 1.0.0
*
* Author : Vedat Taylan
*
* Year : 2018
* Usage : $('#InputID').furl({id:'ReplaceInputID', seperate (optional) : '_' });
*/
(function ($, undefined) {
function Furl() {
this.defaults = {
seperate: '-'
};
}
Furl.prototype = {
init: function (target, options) {
var $this = this;
options = $.extend({}, $this.defaults, options);
$(target).keyup(function () {
var url = urlReplace($(this).val(), options);
var $element = $('#' + options.id);
if ($element.length > 0) {
var tagName = $element.get(0).tagName;
switch (tagName) {
case 'INPUT':
$element.val(url);
break;
default:
$element.text(url);
}
}
});
}
};
function urlReplace(url, options) {
return url.toLowerCase()
.replace(/ä/g, 'ae')
.replace(/Ä/g, 'ae')
.replace(/ğ/g, 'g')
.replace(/ü/g, 'u')
.replace(/ş/g, 's')
.replace(/ı/g, 'i')
.replace(/ö/g, 'o')
.replace(/ç/g, 'c')
.replace(/Ğ/g, 'g')
.replace(/Ü/g, 'u')
.replace(/Ş/g, 's')
.replace(/I/g, 'i')
.replace(/İ/g, 'i')
.replace(/Ö/g, 'o')
.replace(/Ç/g, 'c')
.replace(/[^a-z0-9\s-]/g, "")
.replace(/[\s-]+/g, " ")
.replace(/^\s+|\s+$/g, "")
.replace(/\s/g, "-")
.replace(/^\s+|\s+$/g, "")
.replace(/[_|\s]+/g, "-")
.replace(/[^a-z\u0400-\u04FF0-9-]+/g, "")
.replace(/[-]+/g, "-")
.replace(/^-+|-+$/g, "")
.replace(/[-]+/g, options.seperate);
}
$.furl = new Furl();
$.furl.version = "1.0.0";
$.fn.furl = function (options) {
return this.each(function () {
$.furl.init(this, options);
});
};
})(jQuery);

18
blog/view/add/add.css Normal file
View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

58
blog/view/add/add.js.php Normal file
View File

@ -0,0 +1,58 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/**
* Soumission du formulaire pour enregistrer en brouillon
*/
$("#blogAddDraft").on("click", function() {
$("#blogAddState").val(0);
$("#blogAddForm").trigger("submit");
});
/**
* Options de commentaires
*/
$("#blogAddCommentClose").on("change", function() {
if ($(this).is(':checked') ) {
$(".commentOptionsWrapper").slideUp();
} else {
$(".commentOptionsWrapper").slideDown();
}
});
$("#blogAddCommentNotification").on("change", function() {
if ($(this).is(':checked') ) {
$("#blogAddCommentGroupNotification").slideDown();
} else {
$("#blogAddCommentGroupNotification").slideUp();
}
});
$( document).ready(function() {
if ($("#blogAddCloseComment").is(':checked') ) {
$(".commentOptionsWrapper").slideUp();
} else {
$(".commentOptionsWrapper").slideDown();
}
if ($("#blogAddCommentNotification").is(':checked') ) {
$("#blogAddCommentGroupNotification").slideDown();
} else {
$("#blogAddCommentGroupNotification").slideUp();
}
// Permalink
$('#blogAddTitle').furl({id:'blogAddPermalink', seperate: '_' });
});

137
blog/view/add/add.php Normal file
View File

@ -0,0 +1,137 @@
<?php echo template::formOpen('blogAddForm'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('blogAddBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
'value' => template::ico('left')
]); ?>
</div>
<div class="col2 offset7">
<?php echo template::button('blogAddDraft', [
'uniqueSubmission' => true,
'value' => 'Brouillon'
]); ?>
<?php echo template::hidden('blogAddState', [
'value' => true
]); ?>
</div>
<div class="col2">
<?php echo template::submit('blogAddPublish', [
'value' => 'Publier',
'uniqueSubmission' => true
]); ?>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Informations générales');?></h4>
<div class="row">
<div class="col6">
<?php echo template::text('blogAddTitle', [
'label' => 'Titre'
]); ?>
</div>
<div class="col6">
<?php echo template::text('blogAddPermalink', [
'label' => 'Permalink'
]); ?>
</div>
</div>
<div class="row">
<div class="col4">
<?php echo template::file('blogAddPicture', [
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'help' => 'Taille optimale de l\'image de couverture : ' . ((int) substr($this->getData(['theme', 'site', 'width']), 0, -2) - (20 * 2)) . ' x 350 pixels.',
'label' => 'Image de couverture',
'type' => 1
]); ?>
</div>
<div class="col4">
<?php echo template::select('blogAddPictureSize', $module::$pictureSizes, [
'label' => 'Largeur de l\'image'
]); ?>
</div>
<div class="col4">
<?php echo template::select('blogAddPicturePosition', $module::$picturePositions, [
'label' => 'Position',
'help' => 'Le texte de l\'article est adapté autour de l\'image'
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('blogAddHidePicture', true, 'Masquer l\'image de couverture dans l\'article', [
'checked' => true
]); ?>
</div>
</div>
</div>
</div>
</div>
<?php echo template::textarea('blogAddContent', [
'class' => 'editorWysiwyg'
]); ?>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Options de publication'); ?></h4>
<div class="row">
<div class="col4">
<?php echo template::select('blogAddUserId', $module::$users, [
'label' => 'Auteur',
'selected' => $this->getUser('id'),
'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false
]); ?>
</div>
<div class="col4">
<?php echo template::date('blogAddPublishedOn', [
'help' => 'L\'article n\'est visible qu\'après la date de publication prévue.',
'label' => 'Date de publication',
'type' => 'datetime-local',
'format' => $this->getData(['module', $this->getUrl(0), 'config', 'dateFormat']) . $this->getData(['module', $this->getUrl(0), 'config', 'timeFormat']),
'value' => floor(strtotime('now') / 60) * 60
]); ?>
</div>
<div class="col4">
<?php echo template::select('blogAddConsent', $module::$articleConsent , [
'label' => 'Édition - Suppression',
'selected' => $module::EDIT_ALL,
'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'article sans restriction'
]); ?>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Commentaires');?></h4>
<div class="row">
<div class="col4 ">
<?php echo template::checkbox('blogAddCommentClose', true, 'Fermer les commentaires'); ?>
</div>
<div class="col4 commentOptionsWrapper ">
<?php echo template::checkbox('blogAddCommentApproved', true, 'Approbation par un modérateur'); ?>
</div>
<div class="col4 commentOptionsWrapper">
<?php echo template::select('blogAddCommentMaxlength', $module::$commentsLength,[
'help' => 'Choix du nombre maximum de caractères pour chaque commentaire de l\'article, mise en forme html comprise.',
'label' => 'Caractères par commentaire'
]); ?>
</div>
</div>
<div class="row">
<div class="col3 commentOptionsWrapper offset2">
<?php echo template::checkbox('blogAddCommentNotification', true, 'Notification par email'); ?>
</div>
<div class="col4 commentOptionsWrapper">
<?php echo template::select('blogAddCommentGroupNotification', $module::$groupNews); ?>
</div>
</div>
</div>
</div>
</div>
<?php echo template::formClose(); ?>

View File

@ -0,0 +1,63 @@
#sectionTitle {
margin-top: 0;
margin-bottom: 5px;
}
.blogArticlePicture {
height: auto;
border:1px solid lightgray;
box-shadow: 1px 1px 5px;
}
.blogArticlePictureleft {
float: left;
margin: 15px 10px 5px 0 ;
}
.blogArticlePictureright {
float: right;
margin: 15px 0 5px 10px ;
}
.pict20{
width: 20%;
}
.pict30{
width: 30%;
}
.pict40{
width: 40%;
}
.pict50{
width: 50%;
}
.pict100{
width: 100%;
margin: 15px 0 20px 0 ;
}
#blogArticleCommentShow {
cursor: text;
}
#blogArticleOr {
padding: 10px;
}
.blogDate {
font-style: italic;
color: grey;
height: 100%;
}
@media (max-width: 767px) {
.blogArticlePicture {
height:auto;
max-width: 100%;}
}
#rssFeed {
text-align: right;
float: right;
}
#rssFeed p {
display: inline;
vertical-align: top;
}

View File

@ -0,0 +1,44 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/**
* Affiche le bloc pour rédiger un commentaire
*/
var commentShowDOM = $("#blogArticleCommentShow");
commentShowDOM.on("click focus", function() {
$("#blogArticleCommentShowWrapper").fadeOut(function() {
$("#blogArticleCommentWrapper").fadeIn();
$("#blogArticleCommentContent").trigger("focus");
});
});
if($("#blogArticleCommentWrapper").find("textarea.notice,input.notice").length) {
commentShowDOM.trigger("click");
}
/**
* Cache le bloc pour rédiger un commentaire
*/
$("#blogArticleCommentHide").on("click focus", function() {
$("#blogArticleCommentWrapper").fadeOut(function() {
$("#blogArticleCommentShowWrapper").fadeIn();
$("#blogArticleCommentContent").val("");
$("#blogArticleCommentAuthor").val("");
});
});
/**
* Force le scroll vers les commentaires en cas d'erreur
*/
$("#blogArticleCommentForm").on("submit", function() {
$(location).attr("href", "#comment");
});

View File

@ -0,0 +1,188 @@
<div class="row">
<div class="col12">
<?php $pictureSize = $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'pictureSize']); ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'hidePicture']) == false) {
echo '<img class="blogArticlePicture blogArticlePicture' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'picturePosition']) .
' pict' . $pictureSize . '" src="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'picture']) .
'" alt="' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'picture']) . '">';
} ?>
<?php echo $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'content']); ?>
</div>
</div>
<div class="row verticalAlignMiddle">
<div class="col6 textAlignLeft">
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'buttonBack'])): ?>
<a href="<?php echo helper::baseUrl() . $this->getUrl(0); ?>">
<?php echo template::ico('left') . helper::translate('Retour'); ?>
</a>
<?php endif; ?>
</div>
<div class="col6 newsDate textAlignRight">
<!-- bloc signature -->
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showPseudo']) === true
): ?>
<?php echo template::ico('user'); ?>
<?php echo $this->signature($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'userId'])) ?>
<?php endif; ?>
<!-- bloc date -->
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true
|| $this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true
): ?>
<?php echo template::ico('calendar-empty', ['margin' => 'left']); ?>
<?php endif; ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true): ?>
<?php echo helper::dateUTF8($module::$dateFormat, $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn']), self::$i18nUI); ?>
<?php endif; ?>
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true
&& $this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true
): ?>
<?php echo '&nbsp;-&nbsp;'; ?>
<?php endif; ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true): ?>
<?php echo helper::dateUTF8($module::$timeFormat, $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn']), self::$i18nUI); ?>
<?php endif; ?>
<!-- Bloc edition -->
<?php if (
$this->isConnected() === true
and
( // Propriétaire
($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'editConsent']) === $module::EDIT_OWNER
and ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'userId']) === $this->getUser('id')
or $this->getUser('group') === self::GROUP_ADMIN)
)
or (
// Groupe
($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'editConsent']) === self::GROUP_ADMIN
or $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'editConsent']) === self::GROUP_EDITOR)
and $this->getUser('group') >= $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'editConsent'])
)
or (
// Tout le monde
$this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'editConsent']) === $module::EDIT_ALL
and $this->getUser('group') >= $module::$actions['config']
)
)
): ?>
&nbsp;-&nbsp;
<a href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1); ?>">
<?php echo template::ico('pencil'); ?> Éditer
</a>
<?php endif; ?>
<!-- Bloc RSS-->
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'feeds'])): ?>
&nbsp;-&nbsp;
<div id="rssFeed">
<a type="application/rss+xml" href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/rss'; ?>"
target="_blank">
<img src='module/blog/ressource/feed-icon-16.gif' />
<?php
echo '<p>' . $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel']) . '</p>';
?>
</a>
</div>
<?php endif; ?>
</div>
</div>
<?php if ($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentClose'])): ?>
<p>Cet article ne reçoit pas de commentaire.</p>
<?php else: ?>
<div class="row">
<div class="col12" id="comment">
<h3>
<?php
echo template::ico('comment', ['margin' => 'right']);
if ($module::$nbCommentsApproved > 0) {
echo $module::$nbCommentsApproved . ' commentaire' . ($module::$nbCommentsApproved > 1 ? 's' : '');
} else {
echo 'Pas encore de commentaire';
}
?>
</h3>
</div>
</div>
<?php echo template::formOpen('blogArticleForm'); ?>
<?php echo template::text('blogArticleCommentShow', [
'placeholder' => 'Rédiger un commentaire...',
'readonly' => true
]); ?>
<div id="blogArticleCommentWrapper" class="displayNone">
<?php if ($this->isConnected() === true): ?>
<?php echo template::text('blogArticleUserName', [
'label' => 'Nom',
'readonly' => true,
'value' => $module::$editCommentSignature
]); ?>
<?php echo template::hidden('blogArticleUserId', [
'value' => $this->getUser('id')
]); ?>
<?php else: ?>
<div class="row">
<div class="col9">
<?php echo template::text('blogArticleAuthor', [
'label' => 'Nom'
]); ?>
</div>
<div class="col1 textAlignCenter verticalAlignBottom">
<div id="blogArticleOr">Ou</div>
</div>
<div class="col2 verticalAlignBottom">
<?php echo template::button('blogArticleLogin', [
'href' => helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl()) . '__comment',
'value' => 'Connexion'
]); ?>
</div>
</div>
<?php endif; ?>
<?php echo template::textarea('blogArticleContent', [
'label' => 'Commentaire avec maximum ' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentMaxlength']) . ' caractères',
'class' => 'editorWysiwygComment',
'noDirty' => true,
'maxlength' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentMaxlength'])
]); ?>
<div id="blogArticleContentAlarm"> </div>
<?php if ($this->isConnected() === false): ?>
<div class="row">
<div class="col12">
<?php echo template::captcha('blogArticleCaptcha', [
'limit' => $this->getData(['config', 'connect', 'captchaStrong']),
'type' => $this->getData(['config', 'connect', 'captchaType'])
]); ?>
</div>
</div>
<?php endif; ?>
<div class="row">
<div class="col2 offset8">
<?php echo template::button('blogArticleCommentHide', [
'class' => 'buttonGrey',
'value' => 'Annuler'
]); ?>
</div>
<div class="col2">
<?php echo template::submit('blogArticleSubmit', [
'value' => 'Envoyer',
'ico' => ''
]); ?>
</div>
</div>
</div>
<?php endif; ?>
<div class="row">
<div class="col12">
<?php foreach ($module::$comments as $commentId => $comment): ?>
<div class="block">
<h4>
<?php echo template::ico('user'); ?>
<?php echo $module::$commentsSignature[$commentId]; ?>
<?php echo template::ico('calendar-empty'); ?>
<?php echo helper::dateUTF8($module::$dateFormat, $comment['createdOn'], self::$i18nUI) . ' - ' . helper::dateUTF8($module::$timeFormat, $comment['createdOn'], self::$i18nUI); ?>
</h4>
<?php echo $comment['content']; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php echo $module::$pages; ?>

View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,61 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/**
* Confirmation de suppression
*/
$(".blogCommentDelete").on("click", function() {
var _this = $(this);
var message = "<?php echo helper::translate('Supprimer le commentaire ?'); ?>";
return core.confirm(message , function() {
$(location).attr("href", _this.attr("href"));
});
});
/**
* Confirmation d'approbation
*/
$(".blogCommentApproved").on("click", function() {
var _this = $(this);
var message = "<?php echo helper::translate('Approuver le commentaire ?'); ?>";
return core.confirm(message , function() {
$(location).attr("href", _this.attr("href"));
});
});
/**
* Confirmation de rejet
*/
$(".blogCommentRejected").on("click", function() {
var _this = $(this);
var message = "<?php echo helper::translate('Rejeter le commentaire ?'); ?>";
return core.confirm(message , function() {
$(location).attr("href", _this.attr("href"));
});
});
/**
* Confirmation de suppression en masse
*/
$(".blogCommentDeleteAll").on("click", function() {
var _this = $(this);
if( nombre === "1"){
var message = "<?php echo helper::translate('Supprimer le commentaire ?'); ?>";
} else {
var message = "<?php echo helper::translate('Supprimer tous les commentairess ?'); ?>";
}
return core.confirm(message, function() {
$(location).attr("href", _this.attr("href"));
});
});

View File

@ -0,0 +1,22 @@
<div class="row">
<div class="col2">
<?php echo template::button('blogCommentBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
'ico' => 'left',
'value' => 'Retour'
]); ?>
</div>
<?php if($module::$comments): ?>
<div class="col2 offset8">
<?php echo $module::$commentsDelete; ?>
</div>
</div>
<?php echo template::table([3, 5, 2, 1, 1], $module::$comments, ['Date', 'Contenu', 'Auteur', '', '']); ?>
<?php echo $module::$pages.'<br/>'; ?>
<?php else: ?>
</div>
<?php echo template::speech('Aucun commentaire'); ?>
<?php endif; ?>

View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,23 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/**
* Confirmation de suppression
*/
$(".blogConfigDelete").on("click", function() {
var _this = $(this);
var message = "<?php echo helper::translate('Supprimer cet article ?'); ?>";
return core.confirm(message , function() {
$(location).attr("href", _this.attr("href"));
});
});

View File

@ -0,0 +1,37 @@
<?php echo template::formOpen('blogConfig'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('blogConfigBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0) . '/' . self::$siteContent,
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset9">
<?php echo template::button('blogConfigOption', [
'href' => helper::baseUrl() . $this->getUrl(0) . '/option',
'value' => template::ico('sliders'),
'help' => 'Options de configuration'
]); ?>
</div>
<div class="col1">
<?php echo template::button('blogConfigAdd', [
'href' => helper::baseUrl() . $this->getUrl(0) . '/add',
'value' => template::ico('plus'),
'class' => 'buttonGreen',
'help' => 'Rédiger un article'
]); ?>
</div>
</div>
<?php echo template::formClose(); ?>
<?php if($module::$articles): ?>
<?php echo template::table([4, 4, 1, 1, 1, 1], $module::$articles, ['Titre', 'Publication', 'État', 'Commentaires', '','']); ?>
<?php echo $module::$pages; ?>
<?php else: ?>
<?php echo template::speech('Aucun article'); ?>
<?php endif; ?>
<div class="moduleVersion">Version
<?php echo $module::VERSION; ?>
</div>

18
blog/view/edit/edit.css Normal file
View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,71 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
// Lien de connexion
$("#blogEditMailNotification").on("change", function() {
if($(this).is(":checked")) {
$("#formConfigGroup").show();
}
else {
$("#formConfigGroup").hide();
}
}).trigger("change");
/**
* Soumission du formulaire pour enregistrer en brouillon
*/
$("#blogEditDraft").on("click", function() {
$("#blogEditState").val(0);
$("#blogEditForm").trigger("submit");
});
/**
* Options de commentaires
*/
$("#blogEditCommentClose").on("change", function() {
if ($(this).is(':checked') ) {
$(".commentOptionsWrapper").slideUp();
} else {
$(".commentOptionsWrapper").slideDown();
}
});
$("#blogEditCommentNotification").on("change", function() {
if ($(this).is(':checked') ) {
$("#blogEditCommentGroupNotification").slideDown();
} else {
$("#blogEditCommentGroupNotification").slideUp();
}
});
$( document).ready(function() {
if ($("#blogEditCloseComment").is(':checked') ) {
$(".commentOptionsWrapper").slideUp();
} else {
$(".commentOptionsWrapper").slideDown();
}
if ($("#blogEditCommentNotification").is(':checked') ) {
$("#blogEditCommentGroupNotification").slideDown();
} else {
$("#blogEditCommentGroupNotification").slideUp();
}
// Permalink
$('#blogEditTitle').furl({id:'blogEditPermalink', seperate: '_' });
});

154
blog/view/edit/edit.php Normal file
View File

@ -0,0 +1,154 @@
<?php echo template::formOpen('blogEditForm'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('blogEditBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
'value' => template::ico('left')
]); ?>
</div>
<div class="col3 offset6">
<?php echo template::button('blogEditDraft', [
'uniqueSubmission' => true,
'value' => 'Brouillon'
]); ?>
<?php echo template::hidden('blogEditState', [
'value' => true
]); ?>
</div>
<div class="col2">
<?php echo template::submit('blogEditSubmit', [
'value' => 'Publier',
'uniqueSubmission' => true
]); ?>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Paramètres'); ?></h4>
<div class="row">
<div class="col6">
<?php echo template::text('blogEditTitle', [
'label' => 'Titre',
'value' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'title'])
]); ?>
</div>
<div class="col6">
<?php echo template::text('blogEditPermalink', [
'label' => 'Permalink',
'value' => $this->getUrl(2)
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::file('blogEditPicture', [
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'help' => $this->getData(['theme', 'site', 'width']) !== '100%' ? 'Taille optimale de l\'image de couverture : ' . ((int) substr($this->getData(['theme', 'site', 'width']), 0, -2) - (20 * 2)) . ' x 350 pixels.' : '',
'label' => 'Image de couverture',
'type' => 1,
'value' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'picture']),
'folder' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'picture']) ? dirname($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'picture'])) : ''
]); ?>
</div>
<div class="col3">
<?php echo template::select('blogEditPictureSize', $module::$pictureSizes, [
'label' => 'Largeur de l\'image',
'selected' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'pictureSize'])
]); ?>
</div>
<div class="col3">
<?php echo template::select('blogEditPicturePosition', $module::$picturePositions, [
'label' => 'Position',
'selected' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'picturePosition']),
'help' => 'Le texte de l\'article est adapté autour de l\'image'
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('blogEditHidePicture', true, 'Masquer l\'image de couverture dans l\'article', [
'checked' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'hidePicture'])
]); ?>
</div>
</div>
</div>
</div>
</div>
<?php echo template::textarea('blogEditContent', [
'class' => 'editorWysiwyg',
'value' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'content'])
]); ?>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Options de publication'); ?></h4>
<div class="row">
<div class="col4">
<?php echo template::select('blogEditUserId', $module::$users, [
'label' => 'Auteur',
'selected' => $this->getUser('id'),
'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false
]); ?>
</div>
<div class="col4">
<?php echo template::date('blogEditPublishedOn', [
'help' => 'L\'article n\'est visible qu\'après la date de publication prévue.',
'type' => 'datetime-local',
'label' => 'Publication',
'value' => floor($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'publishedOn']) / 60) * 60
]); ?>
</div>
<div class="col4">
<?php echo template::select('blogEditConsent', $module::$articleConsent, [
'label' => 'Édition - Suppression',
'selected' => is_numeric($this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'editConsent'])) ? $module::EDIT_GROUP : $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'editConsent']),
'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'article sans restriction'
]); ?>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Commentaires'); ?></h4>
<div class="row">
<div class="col4 ">
<?php echo template::checkbox('blogEditCommentClose', true, 'Fermer les commentaires', [
'checked' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'commentClose'])
]); ?>
</div>
<div class="col4 commentOptionsWrapper ">
<?php echo template::checkbox('blogEditCommentApproved', true, 'Approbation par un modérateur', [
'checked' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'commentApproved']),
''
]); ?>
</div>
<div class="col4 commentOptionsWrapper">
<?php echo template::select('blogEditCommentMaxlength', $module::$commentsLength, [
'help' => 'Choix du nombre maximum de caractères pour chaque commentaire de l\'article, mise en forme html comprise.',
'label' => 'Caractères par commentaire',
'selected' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'commentMaxlength'])
]); ?>
</div>
</div>
<div class="row">
<div class="col3 commentOptionsWrapper offset2">
<?php echo template::checkbox('blogEditCommentNotification', true, 'Notification par email', [
'checked' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'commentNotification']),
]); ?>
</div>
<div class="col4 commentOptionsWrapper">
<?php echo template::select('blogEditCommentGroupNotification', $module::$groupNews, [
'selected' => $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(2), 'commentGroupNotification']),
]); ?>
</div>
</div>
</div>
</div>
</div>
<?php echo template::formClose(); ?>

154
blog/view/index/index.css Normal file
View File

@ -0,0 +1,154 @@
.rowArticle {
margin-bottom: 10px !important;
}
.blogPicture {
float: none;
border: 1px;
}
.blogPicture img {
width: 100%;
height: auto;
/*
border:1px solid lightgray;
box-shadow: 1px 1px 5px darkgray;
*/
}
#sectionTitle {
margin-top: 0;
margin-bottom: 5px;
}
.blogArticlePicture {
height: auto;
border: 1px solid lightgray;
box-shadow: 1px 1px 5px;
}
.blogArticlePictureleft {
float: left;
margin: 15px 10px 5px 0;
}
.blogArticlePictureright {
float: right;
margin: 15px 0 5px 10px;
}
.blogPicture:hover {
opacity: .7;
}
.row:after {
content: " ";
display: table;
clear: both;
}
.blogComment {
padding-right: 10px;
float: right;
}
.blogTitle {
/*background-color: #ECEFF1;*/
padding: 0px;
margin-bottom: 5px;
}
.blogContent {
position: relative;
float: left;
margin-top: 5px;
width: 100%;
}
.blogDate,
.blogEdit {
font-size: 0.8em;
font-style: italic;
/*
color: grey;
*/
}
@media (max-width: 768px) {
.blogContent {
display: none;
}
.blogPicture img {
width: 50%;
display: block;
margin-left: auto;
margin-right: auto;
}
}
.pict20 {
width: 20%;
}
.pict30 {
width: 30%;
}
.pict40 {
width: 40%;
}
.pict50 {
width: 50%;
}
.pict100 {
width: 100%;
margin: 15px 0 20px 0;
}
/*
* Flux RSS
*/
#rssFeed {
text-align: right;
}
#rssFeed p {
display: inline;
vertical-align: top;
}
.rowArticle,
article {
position: relative;
/* Autres styles pour les articles coupés */
}
.readMoreContainer {
position: absolute;
}
.readMoreContainer,
.readMoreModernContainer {
bottom: 0px;
width: 100%;
height: 45%;
background: linear-gradient(180deg, hsla(0, 0%, 100%, 0) 0, #f6f6f6 100%, #f6f6f6);
}
.readMoreButton {
position: absolute;
width: 20%;
bottom: 0px;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
z-index: 99;
background: linear-gradient(180deg, hsla(0, 0%, 100%, 0) 0, #f6f6f6 100%, #f6f6f6);
/* Autres styles pour le bouton */
}

213
blog/view/index/index.php Normal file
View File

@ -0,0 +1,213 @@
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'feeds'])): ?>
<div id="rssFeed">
<a type="application/rss+xml" href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/rss'; ?>" target="_blank">
<img src='module/blog/ressource/feed-icon-16.gif' />
<?php
echo $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel']) ? '<p>' . $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel']) . '</p>' : '';
?>
</a>
</div>
<?php endif; ?>
<?php if ($module::$articles): ?>
<article id="article">
<?php foreach ($module::$articles as $articleId => $article): ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'layout']) === true): ?>
<div class="readMoreModernContainer">
<div class="row">
<div class="col12">
<h2 class="blogTitle">
<a href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/' . $articleId; ?>">
<?php echo $article['title']; ?>
</a>
</h2>
</div>
</div>
<div class="row">
<div class="col6 blogEdit">
<!-- bloc signature -->
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showPseudo']) === true
): ?>
<?php echo template::ico('user'); ?>
<?php echo $this->signature($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'userId'])); ?>
<?php endif; ?>
<!-- bloc Date -->
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true
|| $this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true
): ?>
<?php echo template::ico('calendar-empty', ['margin' => 'left']); ?>
<?php endif; ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true): ?>
<?php echo helper::dateUTF8($module::$dateFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'publishedOn']), self::$i18nUI); ?>
<?php endif; ?>
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true
&& $this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true
): ?>
<?php echo '&nbsp;-&nbsp;'; ?>
<?php endif; ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true): ?>
<?php echo helper::dateUTF8($module::$timeFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'publishedOn']), self::$i18nUI); ?>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col12">
<?php if (
$this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picture']) &&
file_exists(self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picture']))
): ?>
<?php $pictureSize = $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'pictureSize']); ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'hidePicture']) == false) {
echo '<img class="blogArticlePicture blogArticlePicture' . $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picturePosition']) .
' pict' . $pictureSize . '" src="' . helper::baseUrl(false) . self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picture']) .
'" alt="' . $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picture']) . '">';
} ?>
<?php endif; ?>
<?php echo $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'content']); ?>
</div>
</div>
<div class="row">
<div class="col6 blogEdit">
<!-- Bloc edition -->
<?php if (
$this->isConnected() === true
and
( // Propriétaire
($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'editConsent']) === $module::EDIT_OWNER
and ($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'userId']) === $this->getUser('id')
or $this->getUser('group') === self::GROUP_ADMIN)
)
or (
// Groupe
($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'editConsent']) === self::GROUP_ADMIN
or $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'editConsent']) === self::GROUP_EDITOR)
and $this->getUser('group') >= $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'editConsent'])
)
or (
// Tout le monde
$this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'editConsent']) === $module::EDIT_ALL
and $this->getUser('group') >= $module::$actions['config']
)
)
): ?>
<a href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/edit/' . $articleId; ?>">
<?php echo template::ico('pencil'); ?> Éditer
</a>
<?php endif; ?>
</div>
<div class="col6 textAlignRight" id="comment">
<?php if ($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'commentClose'])): ?>
<p>Cet article ne reçoit pas de commentaire.</p>
<?php else: ?>
<p>
<?php echo template::ico('comment', ['margin' => 'right']); ?>
<?php
if ($module::$comments[$articleId] > 0) {
echo '<a href="' . helper::baseUrl() . $this->getUrl(0) . '/' . $articleId . '">';
echo $module::$comments[$articleId] . ' commentaire' . ($module::$comments[$articleId] > 1 ? 's' : '');
echo '</a>';
} else {
echo 'Pas encore de commentaire';
}
?>
</p>
<?php endif; ?>
</div>
</div>
</div>
<?php else: ?>
<div class="row">
<?php if (
$article['picture'] &&
file_exists(self::FILE_DIR . 'source/' . $article['picture'])
): ?>
<div class="col3">
<?php
// Déterminer le nom de la miniature
$parts = pathinfo($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'picture']));
$thumb = 'mini_' . $parts['basename'];
// Créer la miniature si manquante
if (!file_exists(self::FILE_DIR . 'thumb/' . $thumb)) {
$this->makeThumb(
self::FILE_DIR . 'source/' . $article['picture'],
self::FILE_DIR . 'thumb/' . $thumb,
self::THUMBS_WIDTH
);
}
?>
<a href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/' . $articleId; ?>" class="blogPicture">
<img src="<?php echo helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb; ?>"
alt="<?php echo $article['picture']; ?>">
</a>
</div>
<div class="col9">
<?php else: ?>
<div class="col12">
<?php endif; ?>
<h2 class="blogTitle">
<a href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/' . $articleId; ?>">
<?php echo $article['title']; ?>
</a>
</h2>
<div class="blogComment">
<a href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/' . $articleId; ?>#comment">
<?php if ($module::$comments[$articleId]): ?>
<?php echo $module::$comments[$articleId]; ?>
<?php echo template::ico('comment', ['margin' => 'left']); ?>
<?php endif; ?>
</a>
</div>
<div class="blogDate">
<!-- bloc signature -->
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showPseudo']) === true
): ?>
<?php echo template::ico('user'); ?>
<?php echo $this->signature($this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'userId'])); ?>
<?php endif; ?> <!-- bloc date -->
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true
|| $this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true
): ?>
<?php echo template::ico('calendar-empty', ['margin' => 'left']); ?>
<?php endif; ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true): ?>
<?php echo helper::dateUTF8($module::$dateFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'publishedOn']), self::$i18nUI); ?>
<?php endif; ?>
<?php if (
$this->getData(['module', $this->getUrl(0), 'config', 'showDate']) === true
&& $this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true
): ?>
<?php echo '&nbsp;-&nbsp;'; ?>
<?php endif; ?>
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'showTime']) === true): ?>
<?php echo helper::dateUTF8($module::$timeFormat, $this->getData(['module', $this->getUrl(0), 'posts', $articleId, 'publishedOn']), self::$i18nUI); ?>
<?php endif; ?>
<div class="blogContent">
<?php $lenght = $this->getData(['module', $this->getUrl(0), 'config', 'articlesLenght']); ?>
<?php if ($lenght > 0): ?>
<?php ?>
<?php echo helper::subword($article['content'], 0, $lenght); ?>...
<div class="readMoreContainer">
<a href="<?php echo helper::baseUrl() . $this->getUrl(0) . '/' . $articleId; ?>">
<button class="readMoreButton">
<?php echo helper::translate('Lire la suite'); ?>
</button>
</a>
</div>
<?php else: ?>
<?php echo $article['content']; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</article>
<?php echo $module::$pages; ?>
<?php else: ?>
<?php echo template::speech('Aucun article'); ?>
<?php endif; ?>

View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,40 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
$(document).ready(function() {
// Gestion du changement de la case "Afficher la date"
$('#blogOptionShowDate').change(function() {
var showDateChecked = $(this).is(':checked');
// Afficher ou masquer le wrapper de l'heure selon l'état de la date
if (showDateChecked) {
$('.timeWrapper').show();
} else {
$('.timeWrapper').hide();
// Désactiver l'option "Afficher l'heure" lorsque la date est désactivée
$('#blogOptionShowTime').prop('checked', false).trigger('change');
}
// Afficher ou masquer le format de la date
$('#blogOptionDateFormatWrapper').toggle(showDateChecked);
}).trigger('change'); // Déclenchement au chargement de la page
// Gestion du changement de la case "Afficher l'heure"
$('#blogOptionShowTime').change(function() {
var showTimeChecked = $(this).is(':checked');
// Afficher ou masquer le format de l'heure
$('#blogOptionTimeFormatWrapper').toggle(showTimeChecked);
}).trigger('change'); // Déclenchement au chargement de la page
});

View File

@ -0,0 +1,95 @@
<?php echo template::formOpen('blogOption'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('blogOptionBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
'value' => template::ico('left')
]); ?>
</div>
<div class="col2 offset9">
<?php echo template::submit('blogOptionSubmit'); ?>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4>
<?php echo helper::translate('Paramètres'); ?>
</h4>
<div class="row">
<div class="col6">
<?php echo template::checkbox('blogOptionButtonBack', true, 'Bouton de retour', [
'checked' => $this->getData(['module', $this->getUrl(0), 'config', 'buttonBack'])
]); ?>
</div>
<div class="col6">
<?php echo template::checkbox('blogOptionShowPseudo', true, 'Signature', [
'checked' => $this->getData(['module', $this->getUrl(0), 'config', 'showPseudo'])
]); ?>
</div>
</div>
<div class="row">
<div class="col3">
<?php echo template::checkbox('blogOptionShowDate', true, 'Afficher la date', [
'checked' => $this->getData(['module', $this->getUrl(0), 'config', 'showDate']),
]); ?>
</div>
<div class="col3">
<?php echo template::select('blogOptionDateFormat', $module::$dateFormats, [
'label' => 'Format des dates',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'dateFormat'])
]); ?>
</div>
<div class="col3 timeWrapper">
<?php echo template::checkbox('blogOptionShowTime', true, 'Afficher l\'heure', [
'checked' => $this->getData(['module', $this->getUrl(0), 'config', 'showTime']),
]); ?>
</div>
<div class="col3 timeWrapper">
<?php echo template::select('blogOptionTimeFormat', $module::$timeFormats, [
'label' => 'Format des heures',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'timeFormat'])
]); ?>
</div>
</div>
<div class="row">
<div class="col4">
<?php echo template::select('blogOptionArticlesLayout', $module::$articlesLayout, [
'label' => 'Disposition',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'layout'])
]); ?>
</div>
<div class="col4">
<?php echo template::select('blogOptionArticlesLenght', $module::$articlesLenght, [
'label' => 'Aperçus',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'articlesLenght'])
]); ?>
</div>
<div class="col4">
<?php echo template::select('blogOptionItemsperPage', $module::$ArticlesListed, [
'label' => 'Articles par page',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'itemsperPage'])
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('blogOptionShowFeeds', true, 'Lien du flux RSS', [
'checked' => $this->getData(['module', $this->getUrl(0), 'config', 'feeds']),
]); ?>
</div>
<div class="col6">
<?php echo template::text('blogOptionFeedslabel', [
'label' => 'Texte de l\'étiquette RSS',
'value' => $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel'])
]); ?>
</div>
</div>
</div>
</div>
</div>
<?php echo template::formClose(); ?>
<div class="moduleVersion">Version
<?php echo $module::VERSION; ?>
</div>

1
blog/view/rss/rss.php Normal file
View File

@ -0,0 +1 @@
Le flux RSS est vide ou indisponible !

View File

@ -1 +1 @@
{"name":"calendar","realName":"Calendrier","version":"1.5","update":"0.0","delete":true,"dataDirectory":""}
{"name":"calendar","realName":"Calendrier","version":"1.6","update":"0.0","delete":true,"dataDirectory":""}

37
form/changes.md Normal file
View File

@ -0,0 +1,37 @@
# Versions 4.4
- Le sélecteur de fichier affiche par défaut le chemin vers le fichier présent dans le champ.
# Version 4.3
- Contrôle de la variable de session liée au contenu. Evite des erreurs lorsque plusieurs onglets sont ouverts.
# Version 4.2
- Termes des commandes de profils
# Version 4.1
- Corrige un email non envoyé après validation d'un formulaire.
# Version 4.0
- Gestion des permissions intégrée dans le module.
- Date au format européen.
- Largeur fixe du champ date.
# Version 3.10
- Bloque l'effacement selon le profil
- Masque le code de vérification
- Export des données en CSV impossible
# Version 3.9
- Redirection des pages orphelines
# Version 3.8
- Encodage : UTF-8 de l'objet du message et des noms des champs
# Version 3.7
- Modification liées à la suppression de flatpickr.
# Version 3.6
- Appel de fonction incorrect.
# Version 3.5
- Multilingue
# Version 3.4
- Bug de présentation, une div en trop.
# Version 3.3
- Multilinguisme.
# Version 3.2
- Bug variable non initialisée.
# Version 3.1
- Initialisation des paramètres personnalisés.
# Version 3
- Déplacement des options de formulaires.
- Gabarit du formulaire sur la page.

1
form/enum.json Normal file
View File

@ -0,0 +1 @@
{"name":"form","realName":"Formulaire","version":"4.1","update":"0.0","delete":true,"dataDirectory":""}

543
form/form.php Normal file
View File

@ -0,0 +1,543 @@
<?php
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
class form extends common
{
const VERSION = '4.4';
const REALNAME = 'Formulaire';
const DATADIRECTORY = ''; // Contenu localisé inclus par défaut (page.json et module.json)
public static $actions = [
'config' => self::GROUP_EDITOR,
'option' => self::GROUP_EDITOR,
'data' => self::GROUP_EDITOR,
'delete' => self::GROUP_EDITOR,
'deleteall' => self::GROUP_EDITOR,
'index' => self::GROUP_VISITOR,
'export2csv' => self::GROUP_EDITOR,
];
public static $data = [];
public static $pages = [];
public static $pagination;
// Nombre d'articles dans la page de config:
public static $itemsperPage = 20;
// Objets
const TYPE_MAIL = 'mail';
const TYPE_SELECT = 'select';
const TYPE_TEXT = 'text';
const TYPE_TEXTAREA = 'textarea';
const TYPE_DATETIME = 'date';
const TYPE_CHECKBOX = 'checkbox';
const TYPE_LABEL = 'label';
const ITEMSPAGE = 10;
public static $types = [
self::TYPE_LABEL => 'Étiquette',
self::TYPE_TEXT => 'Champ texte',
self::TYPE_TEXTAREA => 'Grand champ texte',
self::TYPE_MAIL => 'Adresse électronique',
self::TYPE_SELECT => 'Sélection',
self::TYPE_CHECKBOX => 'Case à cocher',
self::TYPE_DATETIME => 'Date'
];
public static $listUsers = [
];
public static $signature = [
'text' => 'Nom du site',
'logo' => 'Logo du site'
];
public static $logoWidth = [
'40' => '40%',
'60' => '60%',
'80' => '80%',
'100' => '100%'
];
public static $optionOffset = [
0 => 'Aucune',
1 => 'Une colonne',
2 => 'Deux colonnes'
];
public static $optionWidth = [
6 => 'Six colonnes',
7 => 'Sept colonnes',
8 => 'Huit colonnes',
9 => 'Neuf colonnes',
10 => 'Dix colonnes',
11 => 'Onze colonnes',
12 => 'Douze colonnes',
];
public static $optionAlign = [
'' => 'A gauche',
'textAlignCenter' => 'Au centre',
'textAlignRight' => 'A droite'
];
// Format fixe temporaire
public static $dateFormat = 'd/m/Y';
/**
* Configuration
*/
public function config()
{
// Mise à jour des données de module
$this->update();
// Liste des utilisateurs
$userIdsFirstnames = helper::arrayColumn($this->getData(['user']), 'firstname');
ksort($userIdsFirstnames);
self::$listUsers[] = '';
foreach ($userIdsFirstnames as $userId => $userFirstname) {
self::$listUsers[] = $userId;
}
// Soumission du formulaire
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
// Génération des données vides
if ($this->getData(['module', $this->getUrl(0), 'data']) === null) {
$this->setData(['module', $this->getUrl(0), 'data', []]);
}
// Génération des champs
$inputs = [];
foreach ($this->getInput('formConfigPosition', null) as $index => $position) {
$inputs[] = [
'name' => html_entity_decode($this->getInput('formConfigName[' . $index . ']')),
'position' => helper::filter($position, helper::FILTER_INT),
'required' => $this->getInput('formConfigRequired[' . $index . ']', helper::FILTER_BOOLEAN),
'type' => $this->getInput('formConfigType[' . $index . ']'),
'values' => $this->getInput('formConfigValues[' . $index . ']')
];
}
$this->setData(['module', $this->getUrl(0), 'input', $inputs]);
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Modifications enregistrées'),
'redirect' => helper::baseUrl() . $this->getUrl(),
'state' => true
]);
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Configuration du module'),
'vendor' => [
'html-sortable',
],
'view' => 'config'
]);
}
public function option()
{
// Liste des utilisateurs
$userIdsFirstnames = helper::arrayColumn($this->getData(['user']), 'firstname');
ksort($userIdsFirstnames);
self::$listUsers[] = '';
foreach ($userIdsFirstnames as $userId => $userFirstname) {
self::$listUsers[] = $userId;
}
// Soumission du formulaire
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$this->isPost()
) {
// Débordement
$width = $this->getInput('formOptionWidth');
if ($this->getInput('formOptionWidth', helper::FILTER_INT) + $this->getInput('formOptionOffset', helper::FILTER_INT) > 12) {
$width = (string) $this->getInput('formOptionWidth', helper::FILTER_INT) - $this->getInput('formOptionOffset', helper::FILTER_INT);
}
// Configuration
$this->setData([
'module',
$this->getUrl(0),
'config',
[
'button' => $this->getInput('formOptionButton'),
'captcha' => $this->getInput('formOptionCaptcha', helper::FILTER_BOOLEAN),
'group' => $this->getInput('formOptionGroup', helper::FILTER_INT),
'user' => self::$listUsers[$this->getInput('formOptionUser', helper::FILTER_INT)],
'mail' => $this->getInput('formOptionMail'),
'pageId' => $this->getInput('formOptionPageIdToggle', helper::FILTER_BOOLEAN) === true ? $this->getInput('formOptionPageId', helper::FILTER_ID) : '',
'subject' => html_entity_decode($this->getInput('formOptionSubject')),
'replyto' => $this->getInput('formOptionMailReplyTo', helper::FILTER_BOOLEAN),
'signature' => $this->getInput('formOptionSignature'),
'logoUrl' => $this->getInput('formOptionLogo'),
'logoWidth' => $this->getInput('formOptionLogoWidth'),
'offset' => $this->getInput('formOptionOffset'),
'width' => $width,
'align' => $this->getInput('formOptionAlign'),
]
]);
// Génération des données vides
if ($this->getData(['module', $this->getUrl(0), 'data']) === null) {
$this->setData(['module', $this->getUrl(0), 'data', []]);
}
// Valeurs en sortie
$this->addOutput([
'notification' => helper::translate('Modifications enregistrées'),
'redirect' => helper::baseUrl() . $this->getUrl(),
'state' => true
]);
} else {
// Liste des pages
foreach ($this->getHierarchy(null, false, false) as $parentPageId => $childrenPageIds) {
self::$pages[$parentPageId] = $this->getData(['page', $parentPageId, 'title']);
foreach ($childrenPageIds as $childKey) {
self::$pages[$childKey] = '&nbsp;»&nbsp;' . $this->getData(['page', $childKey, 'title']);
}
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Options de configuration'),
'vendor' => [
'html-sortable',
],
'view' => 'option'
]);
}
}
/**
* Données enregistrées
*/
public function data()
{
$data = $this->getData(['module', $this->getUrl(0), 'data']);
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) === true &&
$data
) {
// Pagination
$pagination = helper::pagination($data, $this->getUrl(), self::$itemsperPage);
// Liste des pages
self::$pages = $pagination['pages'];
// Inverse l'ordre du tableau
$dataIds = array_reverse(array_keys($data));
$data = array_reverse($data);
// Données en fonction de la pagination
for ($i = $pagination['first']; $i < $pagination['last']; $i++) {
$content = '';
foreach ($data[$i] as $input => $value) {
$content .= $input . ' : ' . $value . '<br>';
}
self::$data[] = [
$content,
template::button('formDataDelete' . $dataIds[$i], [
'class' => 'formDataDelete buttonRed',
'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $dataIds[$i],
'value' => template::ico('trash')
])
];
}
}
// Valeurs en sortie
$this->addOutput([
'title' => helper::translate('Export des données'),
'view' => 'data'
]);
}
/**
* Export CSV
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
*/
public function export2csv()
{
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
} else {
$data = $this->getData(['module', $this->getUrl(0), 'data']);
if ($data !== []) {
$csvfilename = 'data-' . date('dmY') . '-' . date('hm') . '-' . rand(10, 99) . '.csv';
if (!file_exists(self::FILE_DIR . 'source/data')) {
mkdir(self::FILE_DIR . 'source/data', 0755);
}
$fp = fopen(self::FILE_DIR . 'source/data/' . $csvfilename, 'w');
fputcsv($fp, array_keys($data[1]), ';', '"');
foreach ($data as $fields) {
fputcsv($fp, $fields, ';', '"');
}
fclose($fp);
// Valeurs en sortie
$this->addOutput([
'notification' => sprintf(helper::translate('Export CSV effectué dans %s '), $csvfilename ),
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data',
'state' => true
]);
} else {
$this->addOutput([
'notification' => helper::translate('Aucune donnée à exporter'),
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data'
]);
}
}
}
/**
* Suppression
*/
public function deleteall()
{
if (
$this->getUser('permission', __CLASS__, __FUNCTION__) !== true
) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
} else {
$data = ($this->getData(['module', $this->getUrl(0), 'data']));
if (count($data) > 0) {
// Suppression multiple
for ($i = 1; $i <= count($data); $i++) {
$this->deleteData(['module', $this->getUrl(0), 'data', $i]);
}
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data',
'notification' => helper::translate('Données effacées'),
'state' => true
]);
} else {
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data',
'notification' => helper::translate('Aucune donnée à effacer')
]);
}
}
}
/**
* Suppression
*/
public function delete()
{
// Action interdite
if ($this->getUser('permission', __CLASS__, __FUNCTION__) !== true) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
} else {
// La donnée n'existe pas
if ($this->getData(['module', $this->getUrl(0), 'data', $this->getUrl(2)]) === null) {
// Valeurs en sortie
$this->addOutput([
'access' => false
]);
}
// Suppression
else {
$this->deleteData(['module', $this->getUrl(0), 'data', $this->getUrl(2)]);
// Valeurs en sortie
$this->addOutput([
'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data',
'notification' => helper::translate('Donnée effacée'),
'state' => true
]);
}
}
}
/**
* Accueil
*/
public function index()
{
// Mise à jour des données de module
$this->update();
// Soumission du formulaire
if (
$this->isPost()
) {
// Check la captcha
if (
$this->getData(['module', $this->getUrl(0), 'config', 'captcha'])
// AND $this->getInput('formcaptcha', helper::FILTER_INT) !== $this->getInput('formcaptchaFirstNumber', helper::FILTER_INT) + $this->getInput('formcaptchaSecondNumber', helper::FILTER_INT))
and password_verify($this->getInput('formCaptcha', helper::FILTER_INT), $this->getInput('formCaptchaResult')) === false
) {
self::$inputNotices['formCaptcha'] = helper::translate('Captcha incorrect');
}
// Préparation le contenu du mail
$data = [];
$replyTo = null;
$content = '';
foreach ($this->getData(['module', $this->getUrl(0), 'input']) as $index => $input) {
// Filtre la valeur
switch ($input['type']) {
case self::TYPE_MAIL:
$filter = helper::FILTER_MAIL;
break;
case self::TYPE_TEXTAREA:
$filter = helper::FILTER_STRING_LONG;
break;
case self::TYPE_DATETIME:
$filter = helper::FILTER_STRING_SHORT; // Mettre TYPE_DATETIME pour récupérer un TIMESTAMP
break;
case self::TYPE_CHECKBOX:
$filter = helper::FILTER_BOOLEAN;
break;
default:
$filter = helper::FILTER_STRING_SHORT;
}
// Application des filtres
$value = $this->getInput('formInput[' . $index . ']', $filter, $input['required']) === true ? 'X' : $this->getInput('formInput[' . $index . ']', $filter, $input['required']);
// Convertit la date au format correct
$value = $input['type'] === self::TYPE_DATETIME ? date_format(date_create($value), 'd/m/Y') : $value;
// premier champ email ajouté au mail en reply si option active
if (
$this->getData(['module', $this->getUrl(0), 'config', 'replyto']) === true &&
$input['type'] === 'mail'
) {
$replyTo = $value;
}
// Préparation des données pour la création dans la base
$data[$this->getData(['module', $this->getUrl(0), 'input', $index, 'name'])] = $value;
// Préparation des données pour le mail
$content .= '<strong>' . $this->getData(['module', $this->getUrl(0), 'input', $index, 'name']) . ' :</strong> ' . $value . '<br>';
}
// Crée les données
$this->setData(['module', $this->getUrl(0), 'data', helper::increment(1, $this->getData(['module', $this->getUrl(0), 'data'])), $data]);
// Envoi du mail
// Rechercher l'adresse en fonction du mail
$sent = true;
$singleuser = $this->getData([
'user',
$this->getData(['module', $this->getUrl(0), 'config', 'user']),
'mail'
]);
$singlemail = $this->getData(['module', $this->getUrl(0), 'config', 'mail']);
$group = $this->getData(['module', $this->getUrl(0), 'config', 'group']);
// Verification si le mail peut être envoyé
if (
self::$inputNotices === [] && (
$group > 0 ||
$singleuser !== '' ||
$singlemail !== '')
) {
// Utilisateurs dans le groupe
$to = [];
if ($group > 0) {
foreach ($this->getData(['user']) as $userId => $user) {
if ($user['group'] >= $group) {
$to[] = $user['mail'];
}
}
}
// Utilisateur désigné
if (!empty($singleuser)) {
$to[] = $singleuser;
}
// Mail désigné
if (!empty($singlemail)) {
$to[] = $singlemail;
}
if ($to) {
// Sujet du mail
$subject = $this->getData(['module', $this->getUrl(0), 'config', 'subject']);
if ($subject === '') {
$subject = 'Nouveau message en provenance de votre site';
}
// Envoi le mail
$sent = $this->sendMail(
$to,
$subject,
'Nouveau message en provenance de la page "' . $this->getData(['page', $this->getUrl(0), 'title']) . '" :<br><br>' .
$content,
$replyTo,
$this->getData(['config', 'smtp', 'from'])
);
}
}
// Redirection
$redirect = $this->getData(['module', $this->getUrl(0), 'config', 'pageId']);
// Valeurs en sortie
$this->addOutput([
'notification' => ($sent === true ? helper::translate('Formulaire soumis') : $sent),
'redirect' => $redirect ? helper::baseUrl() . $redirect : '',
'state' => ($sent === true ? true : false),
'vendor' => [
],
]);
}
// Valeurs en sortie
$this->addOutput([
'showBarEditButton' => true,
'showPageContent' => true,
'view' => 'index',
]);
}
/**
* Mise à jour du module
* Appelée par les fonctions index et config
*/
private function update()
{
// le module n'est pas initialisé
if ($this->getData(['module', $this->getUrl(0), 'config']) === NULL) {
$this->init();
}
}
/**
* Initialisation du thème d'un nouveau module
*/
private function init()
{
// Données du module absentes
require_once('module/form/ressource/defaultdata.php');
if ($this->getData(['module', $this->getUrl(0), 'config']) === null) {
$this->setData(['module', $this->getUrl(0), 'config', init::$defaultData]);
}
}
}

57
form/i18n/de.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "An alle Gruppen seitdem",
"A un membre": "Ein Membre",
"A une Adresse électronique": "Hat eine E -Mail -Adresse",
"Alignement du formulaire": "Ausrichtung der Form",
"Aucune donnée": "Keine Daten",
"Aucune donnée à effacer": "Keine Daten zum Löschen",
"Aucune donnée à exporter": "Keine Daten zum Exportieren",
"Aucune option pour une étiquette": "Keine Option für ein Etikett",
"Captcha incorrect": "Captcha falsch",
"Case à cocher": "Kontrollkästchen",
"Champ obligatoire": "Pflichtfeld",
"Champ texte": "Textfeld",
"Courriel": "Email",
"Deux colonnes": "Zwei Spalten",
"Dix colonnes": "Zehn Spalten",
"Données effacées": "Gelöschte Daten",
"Douze colonnes": "Zwölf Spalten",
"Décalage à gauche": "Linksverzögerung",
"Effacer toutes les données": "Alle Daten löschen",
"Envoyer": "Schicken",
"Export des données": "Datenexport",
"Exporter toutes les données": "Alle Daten exportieren",
"Formulaire soumis": "Formular eingereicht",
"Gabarit": "Vorlage",
"Grand champ texte": "Großes Textfeld",
"Huit colonnes": "Acht Spalten",
"Largeur du logo": "Logobreite",
"Le formulaire ne contient aucun champ.": "Das Formular enthält kein Feld.",
"Liste des champs": "Feldliste",
"Logo du site": "Logo Sie Site",
"Neuf colonnes": "Neun Spalten",
"Nom du site": "Name der Seite",
"Onze colonnes": "Elf Säulen",
"Options de configuration": "Optionen de Konfiguration",
"Page du site": "Site -Seite",
"Redirection après soumission du formulaire": "Umleitung nach Einreichung des Formulars",
"Sept colonnes": "Sieben Spalten",
"Six colonnes": "Sechs Spalten",
"Sujet du mail": "Mail -Betreff",
"Supprimer cet enregistrement ?": "Diese Aufnahme löschen?",
"Supprimer tous les enregistrements ?": "Alle Aufnahmen löschen?",
"Sélection": "Auswahl",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "Wählen Sie mindestens eine Gruppe, einen Benutzer, ein Benutzer oder geben Sie eine E -Mail ein. Ihr Server muss E -Mail -Sendungen zulassen.",
"Type de signature": "Typ de Signature",
"Une colonne": "Eine Kolumne",
"Validation du formulaire": "Validierung des Formulars",
"Vide affiche le texte par défaut": "Das Vakuum zeigt den Standardtext an",
"Voir et exporter les données du formulaire": "Sehen und exportieren Sie Formulardaten",
"Étiquette": "Etikett",
"Étiquette du bouton de soumission": "Schaltfläche Einsender",
"Options du formulaire": "Formularoptionen",
"Consulter les réponses": "Antworten anzeigen",
"Effacer une réponse": "Antwort löschen",
"Effacer toutes les réponses": "Alle Antworten löschen",
"Exporter toutes les réponses": "Alle Antworten exportieren"
}

57
form/i18n/en_EN.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "To all groups since",
"A un membre": "A membre",
"A une Adresse électronique": "At an email address",
"Alignement du formulaire": "Form alignment",
"Aucune donnée": "No data",
"Aucune donnée à effacer": "No data to erase",
"Aucune donnée à exporter": "No data to export",
"Aucune option pour une étiquette": "No option for a label",
"Captcha incorrect": "Captcha incorrect",
"Case à cocher": "Check box",
"Champ obligatoire": "Required Field",
"Champ texte": "Text field",
"Courriel": "E-mail",
"Deux colonnes": "Two columns",
"Dix colonnes": "Ten columns",
"Données effacées": "Erased data",
"Douze colonnes": "Twelve columns",
"Décalage à gauche": "Left lag",
"Effacer toutes les données": "Erase all the data",
"Envoyer": "Send",
"Export des données": "Data export",
"Exporter toutes les données": "Export all data",
"Formulaire soumis": "Submitted form",
"Gabarit": "Template",
"Grand champ texte": "Large text field",
"Huit colonnes": "Eight columns",
"Largeur du logo": "Logo width",
"Le formulaire ne contient aucun champ.": "The form contains no field.",
"Liste des champs": "Field list",
"Logo du site": "Logo you site",
"Neuf colonnes": "Nine columns",
"Nom du site": "name of the site",
"Onze colonnes": "Eleven columns",
"Options de configuration": "Configuration options",
"Page du site": "Site page",
"Redirection après soumission du formulaire": "Redirection after submission of the form",
"Sept colonnes": "Seven columns",
"Six colonnes": "Six columns",
"Sujet du mail": "Mail subject",
"Supprimer cet enregistrement ?": "Delete this recording?",
"Supprimer tous les enregistrements ?": "Delete all recordings?",
"Sélection": "Select",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "Select at least one group, a user or enter an email. Your server must allow email shipments.",
"Type de signature": "Type de signature",
"Une colonne": "A column",
"Validation du formulaire": "Form validation",
"Vide affiche le texte par défaut": "Vacuum displays the default text",
"Voir et exporter les données du formulaire": "See and export form data",
"Étiquette": "Label",
"Étiquette du bouton de soumission": "Submission button label",
"Options du formulaire": "Form Options",
"Consulter les réponses": "View Responses",
"Effacer une réponse": "Delete a Response",
"Effacer toutes les réponses": "Delete All Responses",
"Exporter toutes les réponses": "Export All Responses"
}

57
form/i18n/es.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "A todos los grupos desde",
"A un membre": "A un miembro",
"A une Adresse électronique": "A una dirección de correo electrónico",
"Alignement du formulaire": "Alineación de formulario",
"Aucune donnée": "Sin datos",
"Aucune donnée à effacer": "No hay datos que borrar",
"Aucune donnée à exporter": "No hay datos para exportar",
"Aucune option pour une étiquette": "No hay opción para una etiqueta",
"Captcha incorrect": "Captcha incorrecto",
"Case à cocher": "Casilla de verificación",
"Champ obligatoire": "Áreaobligatoria",
"Champ texte": "Área de texto",
"Courriel": "Correo electrónico",
"Deux colonnes": "Dos columnas",
"Dix colonnes": "Diez columnas",
"Données effacées": "Datos borrados",
"Douze colonnes": "Doce columnas",
"Décalage à gauche": "Shift izquierdo",
"Effacer toutes les données": "Borrar todos los datos",
"Envoyer": "Enviar",
"Export des données": "Exportación de datos",
"Exporter toutes les données": "Exportar todos los datos",
"Formulaire soumis": "Formulario enviado",
"Gabarit": "Plantilla",
"Grand champ texte": "Área de texto grande",
"Huit colonnes": "Ocho columnas",
"Largeur du logo": "Ancho del logotipo",
"Le formulaire ne contient aucun champ.": "El formulario no contiene ningún área.",
"Liste des champs": "Lista de campos",
"Logo du site": "Logotipo del sitio",
"Neuf colonnes": "Nueve columnas",
"Nom du site": "Nombre del sitio",
"Onze colonnes": "Once columnas",
"Options de configuration": "Opciones de configuración",
"Page du site": "Página del sitio",
"Redirection après soumission du formulaire": "Redireccionamiento después del envío del formulario",
"Sept colonnes": "Siete columnas",
"Six colonnes": "Seis columnas",
"Sujet du mail": "Asunto del correo",
"Supprimer cet enregistrement ?": "¿Borrar este registro?",
"Supprimer tous les enregistrements ?": "¿Borrar todos los registros?",
"Sélection": "Selección",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "Seleccione al menos un grupo, un usuario o ingrese un correo electrónico. Su servidor debe permitir el envío de correo.",
"Type de signature": "Tipo de firma",
"Une colonne": "Una columna",
"Validation du formulaire": "Validación del formulario",
"Vide affiche le texte par défaut": "Vacío muestra el texto predeterminado",
"Voir et exporter les données du formulaire": "Ver y exportar datos del formulario",
"Étiquette": "Etiqueta",
"Étiquette du bouton de soumission": "Etiqueta del botón de envio",
"Options du formulaire": "Opciones del formulario",
"Consulter les réponses": "Consultar respuestas",
"Effacer une réponse": "Eliminar una respuesta",
"Effacer toutes les réponses": "Eliminar todas las respuestas",
"Exporter toutes les réponses": "Exportar todas las respuestas"
}

57
form/i18n/fr_FR.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "",
"A un membre": "",
"A une Adresse électronique": "",
"Alignement du formulaire": "",
"Aucune donnée": "",
"Aucune donnée à effacer": "",
"Aucune donnée à exporter": "",
"Aucune option pour une étiquette": "",
"Captcha incorrect": "",
"Case à cocher": "",
"Champ obligatoire": "",
"Champ texte": "",
"Courriel": "",
"Deux colonnes": "",
"Dix colonnes": "",
"Données effacées": "",
"Douze colonnes": "",
"Décalage à gauche": "",
"Effacer toutes les données": "",
"Envoyer": "",
"Export des données": "",
"Exporter toutes les données": "",
"Formulaire soumis": "",
"Gabarit": "",
"Grand champ texte": "",
"Huit colonnes": "",
"Largeur du logo": "",
"Le formulaire ne contient aucun champ.": "",
"Liste des champs": "",
"Logo du site": "",
"Neuf colonnes": "",
"Nom du site": "",
"Onze colonnes": "",
"Options de configuration": "",
"Page du site": "",
"Redirection après soumission du formulaire": "",
"Sept colonnes": "",
"Six colonnes": "",
"Sujet du mail": "",
"Supprimer cet enregistrement ?": "",
"Supprimer tous les enregistrements ?": "",
"Sélection": "",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "",
"Type de signature": "",
"Une colonne": "",
"Validation du formulaire": "",
"Vide affiche le texte par défaut": "",
"Voir et exporter les données du formulaire": "",
"Étiquette": "",
"Étiquette du bouton de soumission": "",
"Options du formulaire": "",
"Consulter les réponses": "",
"Effacer une réponse": "",
"Effacer toutes les réponses": "",
"Exporter toutes les réponses": ""
}

57
form/i18n/gr_GR.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "Προς όλες τις ομάδες από την",
"A un membre": "Προς ένα μέλος",
"A une Adresse électronique": "Σε μια διεύθυνση email",
"Alignement du formulaire": "Στοίχιση φόρμας",
"Aucune donnée": "Χωρίς δεδομένα",
"Aucune donnée à effacer": "Δεν υπάρχουν δεδομένα προς διαγραφή",
"Aucune donnée à exporter": "Δεν υπάρχουν δεδομένα προς εξαγωγή",
"Aucune option pour une étiquette": "Δεν υπάρχει επιλογή για ετικέτα",
"Captcha incorrect": "Λανθασμένο captcha",
"Case à cocher": "τσεκάρετε το πλαίσιο",
"Champ obligatoire": "Υποχρεωτικό πεδίο κειμένου",
"Champ texte": "περιοχή κειμένου",
"Courriel": "Ηλεκτρονικό ταχυδρομείο",
"Deux colonnes": "2 στήλες",
"Dix colonnes": "10 στήλες",
"Données effacées": "Διαγραμμένα δεδομένα",
"Douze colonnes": "12 στήλες",
"Décalage à gauche": "Αριστερή μετατόπιση",
"Effacer toutes les données": "Διαγραφή όλων των δεδομένων",
"Envoyer": "Στείλετε",
"Export des données": "ξαγωγή δεδομένων",
"Exporter toutes les données": "Εξαγωγή όλων των δεδομένων",
"Formulaire soumis": "",
"Gabarit": "Πρότυπο",
"Grand champ texte": "μεγάλη περιοχή κειμένου",
"Huit colonnes": "8 στήλες",
"Largeur du logo": "Πλάτος λογότυπου",
"Le formulaire ne contient aucun champ.": "Η φόρμα δεν περιέχει κανένα πεδίο.",
"Liste des champs": "Κατάλογος πεδίων",
"Logo du site": "Λογότυπο ιστότοπου",
"Neuf colonnes": "9 στήλες",
"Nom du site": "Όνομα ιστότοπου",
"Onze colonnes": "11 στήλες",
"Options de configuration": "Επιλογές διαμόρφωσης",
"Page du site": "Σελίδα ιστότοπου",
"Redirection après soumission du formulaire": "Ανακατεύθυνση μετά την υποβολή της φόρμας",
"Sept colonnes": "7 στήλες",
"Six colonnes": "6 στήλες",
"Sujet du mail": "Θέμα αλληλογραφίας",
"Supprimer cet enregistrement ?": "Να διαγράψω αυτό το αρχείο;",
"Supprimer tous les enregistrements ?": "Διαγραφή όλων των εγγραφών;",
"Sélection": "Επιλογή",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "Επιλέξτε τουλάχιστον μία ομάδα, έναν χρήστη ή εισαγάγετε ένα email. Ο διακομιστής σας πρέπει να επιτρέπει την αποστολή αλληλογραφίας",
"Type de signature": "Τύπος υπογραφής",
"Une colonne": "1 στήλη",
"Validation du formulaire": "Επικύρωση της φόρμας",
"Vide affiche le texte par défaut": "Το κενό εμφανίζει προεπιλεγμένο κείμενο",
"Voir et exporter les données du formulaire": "Προβολή και εξαγωγή δεδομένων φόρμας",
"Étiquette": "ετικέτα",
"Étiquette du bouton de soumission": "Ετικέτα κουμπιού υποβολής",
"Options du formulaire": "Επιλογές φόρμας",
"Consulter les réponses": "Προβολή απαντήσεων",
"Effacer une réponse": "Διαγραφή μιας απάντησης",
"Effacer toutes les réponses": "Διαγραφή όλων των απαντήσεων",
"Exporter toutes les réponses": "Εξαγωγή όλων των απαντήσεων"
}

57
form/i18n/it.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "A tutti i gruppi da allora",
"A un membre": "A un membre",
"A une Adresse électronique": "A un indirizzo email",
"Alignement du formulaire": "Allineamento della forma",
"Aucune donnée": "Nessun dato",
"Aucune donnée à effacer": "Nessun dato da cancellare",
"Aucune donnée à exporter": "Nessun dato da esportare",
"Aucune option pour une étiquette": "Nessuna opzione per un'etichetta",
"Captcha incorrect": "Captcha errato",
"Case à cocher": "Casella di controllo",
"Champ obligatoire": "Campo obbligatorio",
"Champ texte": "Campo di testo",
"Courriel": "E-mail",
"Deux colonnes": "Due colonne",
"Dix colonnes": "Dieci colonne",
"Données effacées": "Dati cancellati",
"Douze colonnes": "Dodici colonne",
"Décalage à gauche": "Lag sinistra",
"Effacer toutes les données": "Cancella tutti i dati",
"Envoyer": "mandare",
"Export des données": "sportazione dati",
"Exporter toutes les données": "Esporta tutti i dati",
"Formulaire soumis": "Modulo inviato",
"Gabarit": "Modello",
"Grand champ texte": "Grande campo di testo",
"Huit colonnes": "Otto colonne",
"Largeur du logo": "Larghezza del logo",
"Le formulaire ne contient aucun champ.": "Il modulo non contiene campo.",
"Liste des champs": "Elenco dei campi",
"Logo du site": "Logo il tuo sito",
"Neuf colonnes": "Nove colonne",
"Nom du site": "Nome del sito",
"Onze colonnes": "Undici colonne",
"Options de configuration": "Opzioni di configurazione",
"Page du site": "Pagina del sito",
"Redirection après soumission du formulaire": "Reindirizzamento dopo la presentazione del modulo",
"Sept colonnes": "Sette colonne",
"Six colonnes": "Sei colonne",
"Sujet du mail": "Oggetto di posta",
"Supprimer cet enregistrement ?": "Elimina questa registrazione?",
"Supprimer tous les enregistrements ?": "Elimina tutte le registrazioni?",
"Sélection": "Selezione",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "Seleziona almeno un gruppo, un utente o inserisci un'e -mail. Il tuo server deve consentire le spedizioni e -mail.",
"Type de signature": "Tipo di firma",
"Une colonne": "Una colonna",
"Validation du formulaire": "Convalida del modulo",
"Vide affiche le texte par défaut": "Il vuoto visualizza il testo predefinito",
"Voir et exporter les données du formulaire": "Vedi ed esporta i dati del modulo",
"Étiquette": "Etichetta",
"Étiquette du bouton de soumission": "Etichetta del pulsante di invio",
"Options du formulaire": "Opzioni del modulo",
"Consulter les réponses": "Visualizza risposte",
"Effacer une réponse": "Cancella una risposta",
"Effacer toutes les réponses": "Cancella tutte le risposte",
"Exporter toutes les réponses": "Esporta tutte le risposte"
}

57
form/i18n/pt_PT.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "Para todos os grupos desde",
"A un membre": "Um membre",
"A une Adresse électronique": "Em um endereço de e -mail",
"Alignement du formulaire": "Alinhamento de forma",
"Aucune donnée": "Sem dados",
"Aucune donnée à effacer": "Sem dados para apagar",
"Aucune donnée à exporter": "Sem dados para exportar",
"Aucune option pour une étiquette": "Nenhuma opção para um rótulo",
"Captcha incorrect": "Captcha incorreto",
"Case à cocher": "Caixa de seleção",
"Champ obligatoire": "Campo obrigatório",
"Champ texte": "Campo de texto",
"Courriel": "O email",
"Deux colonnes": "Duas colunas",
"Dix colonnes": "Dez colunas",
"Données effacées": "Dados apagados",
"Douze colonnes": "Doze colunas",
"Décalage à gauche": "Lag esquerdo",
"Effacer toutes les données": "Apague todos os dados",
"Envoyer": "Mandar",
"Export des données": "Exportação de dados",
"Exporter toutes les données": "Exportar todos os dados",
"Formulaire soumis": "Formulário enviado",
"Gabarit": "Modelo",
"Grand champ texte": "Campo de texto grande",
"Huit colonnes": "Oito colunas",
"Largeur du logo": "Largura do logotipo",
"Le formulaire ne contient aucun champ.": "O formulário não contém campo.",
"Liste des champs": "Lista de campo",
"Logo du site": "Logotipo seu site",
"Neuf colonnes": "Nove colunas",
"Nom du site": "nome do site",
"Onze colonnes": "Onze colunas",
"Options de configuration": "Opções de configuração",
"Page du site": "Página do site",
"Redirection après soumission du formulaire": "Redirecionamento após o envio do formulário",
"Sept colonnes": "Sete colunas",
"Six colonnes": "Seis colunas",
"Sujet du mail": "Assunto do correio",
"Supprimer cet enregistrement ?": "Excluir esta gravação?",
"Supprimer tous les enregistrements ?": "Excluir todas as gravações?",
"Sélection": "Seleção",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "Selecione pelo menos um grupo, um usuário ou digite um email. Seu servidor deve permitir remessas por e -mail.",
"Type de signature": "Tipo de assinatura",
"Une colonne": "Uma coluna",
"Validation du formulaire": "Validação do formulário",
"Vide affiche le texte par défaut": "A vácuo exibe o texto padrão",
"Voir et exporter les données du formulaire": "Ver e exportar dados de formulário",
"Étiquette": "Etiqueta",
"Étiquette du bouton de soumission": "Etiqueta do botão de envio",
"Options du formulaire": "Opções do formulário",
"Consulter les réponses": "Ver respostas",
"Effacer une réponse": "Apagar uma resposta",
"Effacer toutes les réponses": "Apagar todas as respostas",
"Exporter toutes les réponses": "Exportar todas as respostas"
}

57
form/i18n/tr_TR.json Normal file
View File

@ -0,0 +1,57 @@
{
"A tous les groupes depuis": "Şu tarihten itibaren tüm gruplara",
"A un membre": "Bir üyeye",
"A une Adresse électronique": "Bir E-posta Adresine",
"Alignement du formulaire": "Form Hizalama",
"Aucune donnée": "Veri yok",
"Aucune donnée à effacer": "Silinecek veri yok",
"Aucune donnée à exporter": "Dışa aktarılacak veri yok",
"Aucune option pour une étiquette": "Etiket seçeneği yok",
"Captcha incorrect": "Yanlış Captcha",
"Case à cocher": "Onay kutusu",
"Champ obligatoire": "Gerekli alan",
"Champ texte": "Metin alanı",
"Courriel": "E-posta",
"Deux colonnes": "İki sütun",
"Dix colonnes": "On sütun",
"Données effacées": "Veriler silindi",
"Douze colonnes": "On iki sütun",
"Décalage à gauche": "Sola kaydirma",
"Effacer toutes les données": "Tüm verileri temizle",
"Envoyer": "Göndermek",
"Export des données": "Verileri dışa aktarma",
"Exporter toutes les données": "Tüm verileri dışa aktar",
"Formulaire soumis": "Form gönderildi",
"Gabarit": "Şablon",
"Grand champ texte": "Geniş metin alanı",
"Huit colonnes": "Sekiz sütun",
"Largeur du logo": "Logo genişliği",
"Le formulaire ne contient aucun champ.": "Form herhangi bir alan içermiyor.",
"Liste des champs": "Alanların listesi",
"Logo du site": "Site logosu",
"Neuf colonnes": "Dokuz sütun",
"Nom du site": "Sitenin adı",
"Onze colonnes": "On bir sütun",
"Options de configuration": "Yapılandırma seçenekleri",
"Page du site": "Site sayfası",
"Redirection après soumission du formulaire": "Form gönderiminden sonra yönlendirme",
"Sept colonnes": "Yedi sütun",
"Six colonnes": "Altı sütun",
"Sujet du mail": "Posta konusu",
"Supprimer cet enregistrement ?": "Bu kayıt silinsin mi?",
"Supprimer tous les enregistrements ?": "Tüm kayıtlar silinsin mi?",
"Sélection": "Seçim",
"Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.": "En az bir grup, bir kullanıcı seçin veya bir e-posta girin. Sunucunuz posta göndermeye izin vermelidir.",
"Type de signature": "İmza türü",
"Une colonne": "Bir sütun",
"Validation du formulaire": "Form doğrulama",
"Vide affiche le texte par défaut": "Boş ise, varsayılan metni görüntüler",
"Voir et exporter les données du formulaire": "Form verilerini görüntüle ve dışa aktar",
"Étiquette": "Etiket",
"Étiquette du bouton de soumission": "Düğme etiketini gönder",
"Options du formulaire": "Form Seçenekleri",
"Consulter les réponses": "Yanıtları Görüntüle",
"Effacer une réponse": "Bir Yanıtı Sil",
"Effacer toutes les réponses": "Tüm Yanıtları Sil",
"Exporter toutes les réponses": "Tüm Yanıtları Dışa Aktar"
}

View File

@ -0,0 +1,12 @@
<?php $moduleData['form'] = [
'option' => $this->getInput('profilAddFormOption', helper::FILTER_BOOLEAN),
'data' => $this->getInput('profilAddFormData', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilAddFormDelete', helper::FILTER_BOOLEAN),
'deleteAll' => $this->getInput('profilAddFormDeleteAll', helper::FILTER_BOOLEAN),
'export2csv' => $this->getInput('profilAddFormExport2csv', helper::FILTER_BOOLEAN),
'config' => $this->getInput('profilAddFormOption', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddFormData', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddFormDelete', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddFormDeleteAll', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddFormExport2csv', helper::FILTER_BOOLEAN)
];

View File

@ -0,0 +1,8 @@
<?php $moduleData['form'] = [
'config' => $this->getInput('profilEditFormConfig', helper::FILTER_BOOLEAN),
'option' => $this->getInput('profilEditFormOption', helper::FILTER_BOOLEAN),
'data' => $this->getInput('profilEditFormData', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilEditFormDelete', helper::FILTER_BOOLEAN),
'deleteAll' => $this->getInput('profilEditFormDeleteAll', helper::FILTER_BOOLEAN),
'export2csv' => $this->getInput('profilEditFormExport2csv', helper::FILTER_BOOLEAN),
];

View File

@ -0,0 +1,28 @@
<div class="row">
<div class="col12">
<div class="block">
<h4>
<?php echo sprintf('%s %s', helper::translate('Permissions'), helper::translate('Formulaire')); ?>
</h4>
<div class="row">
<div class="col6">
<?php echo template::checkbox('profilEditFormOption', true, 'Options du formulaire'); ?>
</div>
<div class="col6">
<?php echo template::checkbox('profilEditFormData', true, 'Consulter les réponses'); ?>
</div>
</div>
<div class="row">
<div class="col4">
<?php echo template::checkbox('profilEditFormDelete', true, 'Effacer une réponse'); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilEditFormDeleteAll', true, 'Effacer toutes les réponses'); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilEditFormExport2csv', true, 'Exporter toutes les réponses'); ?>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,38 @@
<div class="row">
<div class="col12">
<div class="block">
<h4>
<?php echo sprintf('%s %s', helper::translate('Permissions'), helper::translate('Formulaire')); ?>
</h4>
<div class="row">
<div class="col6">
<?php echo template::checkbox('profilEditFormOption', true, 'Options du formulaire', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'form', 'option'])
]); ?>
</div>
<div class="col6">
<?php echo template::checkbox('profilEditFormData', true, 'Consulter les réponses', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'form', 'data'])
]); ?>
</div>
</div>
<div class="row">
<div class="col4">
<?php echo template::checkbox('profilEditFormDelete', true, 'Effacer une réponse', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'form', 'delete'])
]); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilEditFormDeleteAll', true, 'Effacer toutes les réponses', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'form', 'deleteAll'])
]); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilEditFormExport2csv', true, 'Exporter toutes les réponses', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'form', 'export2csv'])
]); ?>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,19 @@
<?php
class init extends form {
public static $defaultData = [
'button'=> '',
'captcha'=> false,
'group'=> 0,
'user'=> '',
'mail'=> '',
'pageId'=> '',
'subject'=> '',
'replyto'=> false,
'signature'=> 'text',
'logoUrl'=> '',
'logoWidth'=> '40',
'offset'=> '0',
'width'=> '12',
'align'=> ''
];
}

File diff suppressed because one or more lines are too long

3
form/vendor/html-sortable/inc.json vendored Normal file
View File

@ -0,0 +1,3 @@
[
"html-sortable.min.js"
]

View File

@ -0,0 +1 @@
https://github.com/lukasoppermann/html5sortable

View File

@ -0,0 +1,19 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,152 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/**
* Ajout d'un champ
*/
function add(inputUid, input) {
// Nouveau champ
var newInput = $($("#formConfigCopy").html());
// Ajout de l'ID unique aux champs
newInput.find("a, input, select").each(function() {
var _this = $(this);
_this.attr({
id: _this.attr("id").replace("[]", "[" + inputUid + "]"),
name: _this.attr("name").replace("[]", "[" + inputUid + "]")
});
});
newInput.find("label").each(function() {
var _this = $(this);
_this.attr("for", _this.attr("for").replace("[]", "[" + inputUid + "]"));
});
// Attribue les bonnes valeurs
if(input) {
// Nom du champ
newInput.find("[name='formConfigName[" + inputUid + "]']").val(input.name);
// Type de champ
newInput.find("[name='formConfigType[" + inputUid + "]']").val(input.type);
// Largeur du champ
newInput.find("[name='formConfigWidth[" + inputUid + "]']").val(input.width);
// Valeurs du champ
newInput.find("[name='formConfigValues[" + inputUid + "]']").val(input.values);
// Champ obligatoire
newInput.find("[name='formConfigRequired[" + inputUid + "]']").prop("checked", input.required);
}
// Ajout du nouveau champ au DOM
$("#formConfigInputs")
.append(newInput.hide())
.find(".formConfigInput").last().show();
// Cache le texte d'absence de champ
$("#formConfigNoInput:visible").hide();
// Check le type
$(".formConfigType").trigger("change");
// Actualise les positions
position();
}
/**
* Afficher/cacher les options supplémentaires
*/
$(document).on("click", ".formConfigMoreToggle", function() {
$(this).parents(".formConfigInput").find(".formConfigMore").slideToggle();
$(this).parents(".formConfigInput").find(".formConfigMoreLabel").slideToggle();
});
/**
* Calcul des positions
*/
function position() {
$("#formConfigInputs").find(".formConfigPosition").each(function(i) {
$(this).val(i + 1);
});
}
/**
* Ajout des champs déjà existant
*/
var inputUid = 0;
var inputs = <?php echo json_encode($this->getData(['module', $this->getUrl(0), 'input'])); ?>;
if(inputs) {
var inputsPerPosition = <?php echo json_encode(helper::arrayColumn($this->getData(['module', $this->getUrl(0), 'input']), 'position', 'SORT_ASC')); ?>;
$.each(inputsPerPosition, function(id) {
add(inputUid, inputs[id]);
inputUid++;
});
}
/**
* Crée un nouveau champ à partir des champs cachés
*/
$("#formConfigAdd").on("click", function() {
add(inputUid);
inputUid++;
});
/**
* Actions sur les champs
*/
// Validation auto après ajout d'un champ
$("a#formConfigAdd.button").click(function () {
$("#formConfigForm").submit();
});
// Tri entre les champs
sortable("#formConfigInputs", {
forcePlaceholderSize: true,
containment: "#formConfigInputs",
handle: ".formConfigMove"
});
$("#formConfigInputs")
// Actualise les positions
.on("sortupdate", function() {
position();
})
// Suppression du champ
.on("click", ".formConfigDelete", function() {
var inputDOM = $(this).parents(".formConfigInput");
// Cache le champ
inputDOM.hide();
// Supprime le champ
inputDOM.remove();
// Affiche le texte d'absence de champ
if($("#formConfigInputs").find(".formConfigInput").length === 0) {
$("#formConfigNoInput").show();
}
// Actualise les positions
position();
})
// Affiche/cache le champ "Valeurs" en fonction des champs cachés
.on("change", ".formConfigType", function() {
var _this = $(this);
switch (_this.val()) {
case "select":
_this.parents(".formConfigInput").find("label[for*=formConfigRequired]").show();
_this.parents(".formConfigInput").find(".formConfigValuesWrapper").slideDown();
_this.parents(".formConfigInput").find(".formConfigLabelWrapper").slideUp();
break;
case "label":
_this.parents(".formConfigInput").find("label[for*=formConfigRequired]").hide();
_this.parents(".formConfigInput").find(".formConfigLabelWrapper").slideDown();
_this.parents(".formConfigInput").find(".formConfigValuesWrapper").slideUp();
break;
default:
_this.parents(".formConfigInput").find("label[for*=formConfigRequired]").show();
_this.parents(".formConfigInput").find(".formConfigValuesWrapper").slideUp();
_this.parents(".formConfigInput").find(".formConfigLabelWrapper").slideUp();
}
});
// Simule un changement de type au chargement de la page
$(".formConfigType").trigger("change");

View File

@ -0,0 +1,97 @@
<div id="formConfigCopy" class="displayNone">
<div class="formConfigInput">
<?php echo template::hidden('formConfigPosition[]', [
'class' => 'formConfigPosition'
]); ?>
<div class="row">
<div class="col1">
<?php echo template::button('formConfigMove[]', [
'value' => template::ico('sort'),
'class' => 'formConfigMove'
]); ?>
</div>
<div class="col5">
<?php echo template::text('formConfigName[]', [
'placeholder' => 'Intitulé'
]); ?>
</div>
<div class="col4">
<?php echo template::select('formConfigType[]', $module::$types, [
'class' => 'formConfigType'
]); ?>
</div>
<div class="col1">
<?php echo template::button('formConfigMoreToggle[]', [
'value' => template::ico('gear'),
'class' => 'formConfigMoreToggle'
]); ?>
</div>
<div class="col1">
<?php echo template::button('formConfigDelete[]', [
'value' => template::ico('minus'),
'class' => 'formConfigDelete'
]); ?>
</div>
</div>
<div class="formConfigMoreLabel displayNone">
<?php echo template::label('formConfigLabel', 'Aucune option pour une étiquette', [
'class' => 'displayNone formConfigLabelWrapper'
]); ?>
</div>
<div class="formConfigMore displayNone">
<?php echo template::text('formConfigValues[]', [
'placeholder' => 'Liste des valeurs séparées par des virgules (valeur1,valeur2,...)',
'class' => 'formConfigValues',
'classWrapper' => 'displayNone formConfigValuesWrapper'
]); ?>
<?php echo template::checkbox('formConfigRequired[]', true, 'Champ obligatoire'); ?>
</div>
</div>
</div>
<?php echo template::formOpen('formConfigForm'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('formConfigBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0) . '/' . self::$siteContent,
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset7">
<?php echo template::button('formConfigData', [
'href' => helper::baseUrl() . $this->getUrl(0) . '/data',
'value' => template::ico('code'),
'help' => 'Voir et exporter les données du formulaire'
]); ?>
</div>
<div class="col1">
<?php echo template::button('formConfigLayout', [
'href' => helper::baseUrl() . $this->getUrl(0) . '/option',
'value' => template::ico('sliders'),
'help' => 'Options de configuration'
]); ?>
</div>
<div class="col2">
<?php echo template::submit('formConfigSubmit'); ?>
</div>
</div>
<div class="block">
<h4><?php echo helper::translate('Liste des champs');?></h4></h4>
<div id="formConfigNoInput">
<?php echo template::speech('Le formulaire ne contient aucun champ.'); ?>
</div>
<div id="formConfigInputs"></div>
<div class="row">
<div class="col1 offset11">
<?php echo template::button('formConfigAdd', [
'value' => template::ico('plus'),
'class' => 'buttonGreen',
]); ?>
</div>
</div>
</div>
</div>
<?php echo template::formClose(); ?>
<div class="moduleVersion">Version
<?php echo $module::VERSION; ?>
</div>

18
form/view/data/data.css Normal file
View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,34 @@
/**
* This file is part of Zwii.
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/**
* Confirmation de suppression
*/
$(".formDataDelete").on("click", function() {
var _this = $(this);
var message = "<?php echo helper::translate('Supprimer cet enregistrement ?'); ?>";
return core.confirm(message, function() {
$(location).attr("href", _this.attr("href"));
});
});
/**
* Confirmation de suppression de toutes les donénes
*/
$(".formDataDeleteAll").on("click", function() {
var _this = $(this);
var message = "<?php echo helper::translate('Supprimer tous les enregistrements ?'); ?>";
return core.confirm(message, function() {
$(location).attr("href", _this.attr("href"));
});
});

33
form/view/data/data.php Normal file
View File

@ -0,0 +1,33 @@
<div class="row">
<div class="col1">
<?php echo template::button('formDataBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
'value' => template::ico('left')
]); ?>
</div>
<div class="col1 offset9">
<?php echo template::button('formDataDeleteAll', [
'class' => 'formDataDeleteAll buttonRed',
'href' => helper::baseUrl() . $this->getUrl(0) . '/deleteall',
'value' => template::ico('trash'),
'help' => 'Effacer toutes les données'
]); ?>
</div>
<div class="col1">
<?php echo template::button('formDataBack', [
'href' => helper::baseUrl() . $this->getUrl(0) . '/export2csv',
'value' => template::ico('download'),
'help' => 'Exporter toutes les données'
]); ?>
</div>
</div>
<?php if($module::$data): ?>
<?php echo template::table([11, 1], $module::$data, ['Données', '']); ?>
<?php echo $module::$pages; ?>
<?php else: ?>
<?php echo template::speech('Aucune donnée'); ?>
<?php endif; ?>
<div class="moduleVersion">Version
<?php echo $module::VERSION; ?>
</div>

31
form/view/index/index.css Normal file
View File

@ -0,0 +1,31 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/* Paramètres de l'étiquette dans form */
.formLabel {
margin-top: 20px;
}
.formLabel hr {
border: 0.5px solid;
margin: 5px 0 5px;
}
.inputDateManagerWrapper {
width: 20%;
}
input[type="date"] {
width: 200px;
text-align: center;
}

View File

@ -0,0 +1,21 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2008-2018, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/**
* Paramétrage du format de date
$(function() {
$(".datepicker").flatpickr({
enableTime: false,
});
});
*/

80
form/view/index/index.php Normal file
View File

@ -0,0 +1,80 @@
<?php if ($this->getData(['module', $this->getUrl(0), 'input'])): ?>
<div class="row <?php echo $this->getData(['module', $this->getUrl(0), 'config', 'align']); ?>">
<div class="<?php
echo 'col' . $this->getData(['module', $this->getUrl(0), 'config', 'width']) . ' ';
echo $this->getData(['module', $this->getUrl(0), 'config', 'offset']) !== 0 ? 'offset' . $this->getData(['module', $this->getUrl(0), 'config', 'offset']) : '';
?>">
<?php echo template::formOpen('formForm'); ?>
<?php foreach ($this->getData(['module', $this->getUrl(0), 'input']) as $index => $input): ?>
<?php if ($input['type'] === $module::TYPE_MAIL): ?>
<?php echo template::mail('formInput[' . $index . ']', [
'id' => 'formInput_' . $index,
'label' => $input['name']
]); ?>
<?php elseif ($input['type'] === $module::TYPE_SELECT): ?>
<?php
$values = array_flip(explode(',', $input['values']));
foreach ($values as $value => $key) {
$values[$value] = trim($value);
}
?>
<?php echo template::select('formInput[' . $index . ']', $values, [
'id' => 'formInput_' . $index,
'label' => $input['name']
]); ?>
<?php elseif ($input['type'] === $module::TYPE_TEXT): ?>
<?php echo template::text('formInput[' . $index . ']', [
'id' => 'formInput_' . $index,
'label' => $input['name']
]); ?>
<?php elseif ($input['type'] === $module::TYPE_TEXTAREA): ?>
<?php echo template::textarea('formInput[' . $index . ']', [
'id' => 'formInput_' . $index,
'label' => $input['name']
]); ?>
<?php elseif ($input['type'] === $module::TYPE_DATETIME): ?>
<?php echo template::date('formInput[' . $index . ']', [
'id' => 'formInput_' . $index,
'label' => $input['name'],
'type' => 'date',
]); ?>
<?php elseif ($input['type'] === $module::TYPE_CHECKBOX): ?>
<?php echo template::checkbox(
'formInput[' . $index . ']',
true, $input['name']
); ?>
<?php elseif ($input['type'] === $module::TYPE_LABEL): ?>
<h3 class='formLabel'>
<?php echo $input['name']; ?>
<hr class="formLabel">
</h3>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<div class="row">
<div class="col12 textAlignCenter">
<?php if ($this->getData(['module', $this->getUrl(0), 'config', 'captcha'])): ?>
<div class="row">
<div class="col12 textAlignCenter">
<?php echo template::captcha('formCaptcha', [
'limit' => $this->getData(['config', 'connect', 'captchaStrong']),
'type' => $this->getData(['config', 'connect', 'captchaType'])
]); ?>
</div>
</div>
<?php endif; ?>
<div class="row">
<div class="col2">
<?php echo template::submit('formSubmit', [
'value' => $this->getData(['module', $this->getUrl(0), 'config', 'button']) ? $this->getData(['module', $this->getUrl(0), 'config', 'button']) : 'Envoyer',
'ico' => ''
]); ?>
</div>
</div>
</div>
</div>
<?php echo template::formClose(); ?>
<?php else: ?>
<?php echo template::speech('Le formulaire ne contient aucun champ.'); ?>
<?php endif; ?>

View File

@ -0,0 +1,18 @@
/**
* This file is part of Zwii.
*
* For full copyright and license information, please see the LICENSE
* file that was distributed with this source code.
*
* @author Rémi Jean <remi.jean@outlook.com>
* @copyright Copyright (C) 2008-2018, Rémi Jean
* @author Frédéric Tempez <frederic.tempez@outlook.com>
* @copyright Copyright (C) 2018-2025, Frédéric Tempez
* @license CC Attribution-NonCommercial-NoDerivatives 4.0 International
* @link http://zwiicms.fr/
*/
/** NE PAS EFFACER
* admin.css
*/

View File

@ -0,0 +1,72 @@
/*
* Affiche/cache les options de la case à cocher du mail
*/
$("#formOptionMailOptionsToggle").on("change", function() {
if($(this).is(":checked")) {
$("#formOptionMailOptions").slideDown();
}
else {
$("#formOptionMailOptions").slideUp(function() {
$("#formOptionGroup").val("");
$("#formOptionSubject").val("");
$("#formOptionMail").val("");
$("#formOptionUser").val("");
});
}
}).trigger("change");
/**
* Affiche/cache les options de la case à cocher de la redirection
*/
$("#formOptionPageIdToggle").on("change", function() {
if($(this).is(":checked")) {
$("#formOptionPageIdWrapper").slideDown();
}
else {
$("#formOptionPageIdWrapper").slideUp(function() {
$("#formOptionPageId").val("");
});
}
}).trigger("change");
/**
* Paramètres par défaut au chargement
*/
$( document ).ready(function() {
/**
* Masquer ou afficher la sélection du logo
*/
if ($("#formOptionSignature").val() !== "text") {
$("#formOptionLogoWrapper").addClass("disabled");
$("#formOptionLogoWrapper").slideDown();
$("#formOptionLogoWidthWrapper").addClass("disabled");
$("#formOptionLogoWidthWrapper").slideDown();
} else {
$("#formOptionLogoWrapper").removeClass("disabled");
$("#formOptionLogoWrapper").slideUp();
$("#formOptionLogoWidthWrapper").removeClass("disabled");
$("#formOptionLogoWidthWrapper").slideUp();
}
});
/**
* Masquer ou afficher la sélection du logo
*/
var formOptionSignatureDOM = $("#formOptionSignature");
formOptionSignatureDOM.on("change", function() {
if ($(this).val() !== "text") {
$("#formOptionLogoWrapper").addClass("disabled");
$("#formOptionLogoWrapper").slideDown();
$("#formOptionLogoWidthWrapper").addClass("disabled");
$("#formOptionLogoWidthWrapper").slideDown();
} else {
$("#formOptionLogoWrapper").removeClass("disabled");
$("#formOptionLogoWrapper").slideUp();
$("#formOptionLogoWidthWrapper").removeClass("disabled");
$("#formOptionLogoWidthWrapper").slideUp();
}
});

155
form/view/option/option.php Normal file
View File

@ -0,0 +1,155 @@
<?php echo template::formOpen('formOptionForm'); ?>
<div class="row">
<div class="col1">
<?php echo template::button('formOptionBack', [
'class' => 'buttonGrey',
'href' => helper::baseUrl() . $this->getUrl(0) . '/config',
'value' => template::ico('left')
]); ?>
</div>
<div class="col2 offset9">
<?php echo template::submit('formOptionSubmit'); ?>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Validation du formulaire'); ?></h4>
<div class="row">
<div class="col6">
<?php echo template::checkbox('formOptionCaptcha', true, 'Captcha', [
'checked' => $this->getData(['module', $this->getUrl(0), 'config', 'captcha'])
]); ?>
</div>
<div class="col6">
<?php echo template::text('formOptionButton', [
'help' => 'Vide affiche le texte par défaut',
'label' => 'Étiquette du bouton de soumission',
'value' => $this->getData(['module', $this->getUrl(0), 'config', 'button'])
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('formOptionPageIdToggle', true, 'Redirection après soumission du formulaire', [
'checked' => (bool) $this->getData(['module', $this->getUrl(0), 'config', 'pageId'])
]); ?>
</div>
<div class="col5">
<?php echo template::select('formOptionPageId', $module::$pages, [
'classWrapper' => 'displayNone',
'label' => 'Page du site',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'pageId'])
]); ?>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Gabarit'); ?></h4>
<div class="row">
<div class="col6">
<?php echo template::select('formOptionAlign', $module::$optionAlign, [
'label' => 'Alignement du formulaire',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'align'])
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::select('formOptionOffset', $module::$optionOffset, [
'label' => 'Décalage à gauche',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'offset'])
]); ?>
</div>
<div class="col6">
<?php echo template::select('formOptionWidth', $module::$optionWidth, [
'label' => 'Largeur',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'width'])
]); ?>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col12">
<div class="block">
<h4><?php echo helper::translate('Courriel'); ?></h4>
<?php echo template::checkbox('formOptionMailOptionsToggle', true, 'Envoyer par mail les données saisies :', [
'checked' => (bool) $this->getData(['module', $this->getUrl(0), 'config', 'group']) ||
!empty($this->getData(['module', $this->getUrl(0), 'config', 'user'])) ||
!empty($this->getData(['module', $this->getUrl(0), 'config', 'mail'])),
'help' => 'Sélectionnez au moins un groupe, un utilisateur ou saisissez un email. Votre serveur doit autoriser les envois de mail.'
]); ?>
<div id="formOptionMailOptions" class="displayNone">
<div class="row">
<div class="col12">
<?php echo template::text('formOptionSubject', [
'help' => 'Vide affiche le texte par défaut',
'label' => 'Sujet du mail',
'value' => $this->getData(['module', $this->getUrl(0), 'config', 'subject'])
]); ?>
</div>
</div>
<?php
// Element 0 quand aucun membre a été sélectionné
$groupMembers = [''] + $module::$groupNews;
?>
<div class="row">
<div class="col4">
<?php echo template::select('formOptionGroup', $groupMembers, [
'label' => 'A tous les groupes depuis',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'group']),
]); ?>
</div>
<div class="col4">
<?php echo template::select('formOptionUser', $module::$listUsers, [
'label' => 'A un membre',
'selected' => array_search($this->getData(['module', $this->getUrl(0), 'config', 'user']), $module::$listUsers)
]); ?>
</div>
<div class="col4">
<?php echo template::text('formOptionMail', [
'label' => 'A une Adresse électronique',
'value' => $this->getData(['module', $this->getUrl(0), 'config', 'mail']),
]); ?>
</div>
</div>
<div class="row">
<div class="col4">
<?php echo template::select('formOptionSignature', $module::$signature, [
'label' => 'Type de signature',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'signature'])
]); ?>
</div>
<div class="col4">
<?php echo template::file('formOptionLogo', [
'language' => $this->getData(['user', $this->getUser('id'), 'language']),
'label' => 'Logo du site',
'value' => $this->getData(['module', $this->getUrl(0), 'config', 'logoUrl']),
'folder' => $this->getData(['module', $this->getUrl(0), 'config', 'logoUrl']) ? dirname($this->getData(['module', $this->getUrl(0), 'config', 'logoUrl'])) : ''
]); ?>
</div>
<div class="col4">
<?php echo template::select('formOptionLogoWidth', $module::$logoWidth, [
'label' => 'Largeur du logo',
'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'logoWidth'])
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('formOptionMailReplyTo', true, 'Répondre à l\'expéditeur depuis le mail de notification', [
'checked' => (bool) $this->getData(['module', $this->getUrl(0), 'config', 'replyto']),
'help' => 'Cette option permet de répondre directement à l\'expéditeur du message si celui-ci a indiqué un email valide.'
]); ?>
</div>
</div>
</div>
</div>
</div>
</div>

18
gallery/changes.md Normal file
View File

@ -0,0 +1,18 @@
# Version 4.2
- Contrôle de la variable de session liée au contenu. Evite des erreurs lorsque plusieurs onglets sont ouverts.
# Version 4.1
- Termes des commandes de profils
# Version 4.0
- Gestion des permissions intégrée dans le module
# Version 3.9
- Bloque l'effacement de la galerie selon le profil
- Masque le code de vérification
# Version 3.8
- Version compare null, dataversion not initialize
- Bug de positionnement des boutons de retour
# Version 3.7
- Multilinguisme
# Version 3.61
- Corrige le retour de $success en lecture écriture de fichier
# Version 3.6
- Appel de la feuille de style si présente

1
gallery/enum.json Normal file
View File

@ -0,0 +1 @@
{"name":"gallery","realName":"Galerie","version":"4.0","update":"0.0","delete":true,"dataDirectory":"site\/data\/gallery\/"}

1050
gallery/gallery.php Normal file

File diff suppressed because it is too large Load Diff

44
gallery/i18n/de.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "Zeigen Sie den Inhalt der Seite mit der Galerie an",
"Alignement du bouton de retour": "Return -Taste -Ausrichtung",
"Alphabétique ": "Alphabetisch",
"Alphabétique inverse": "Alphabetik umgekehrt",
"Au-dessus": "Über",
"Aucune galerie": "Keine Galerie",
"Bordure": "Bordüre",
"Configuration de la galerie %s ": "Galeriekonfiguration %s",
"Configuration des galeries": "Die Konfiguration der Galer",
"Couleur de la bordure": "Randfarbe",
"Couverture": "Decke",
"Discrète": "Diskret",
"Distribué avec marges": "Verteilt",
"Distribué sans marge": "Ohne Rand verteilt",
"Dossier cible": "Zieldatei",
"En dessous": "Unter",
"Epaisse": "Dick",
"Fine": "Bußgeld",
"Forte": "Stärke",
"Galerie ajoutée": "Galerie hinzugefügt",
"Galerie effacée": "Gelobte Galerie",
"Légende": "Legende",
"Légendes": "Legenden",
"Manuel": "Manuel",
"Marge": "Marge",
"Masqué": "Maske",
"Mode plein écran automatique": "Automatischer Vollmodus",
"Opacité au survol": "Opazität im Überflug",
"Options de configuration": "Optionen de Konfiguration",
"Proportionnelle": "Proportional",
"Supprimer cette galerie ?": "Diese Galerie entfernen?",
"Tri des images": "Bilder sortieren",
"Très Discrète": "Sehr diskret",
"Très fine": "Sehr gut",
"Très forte": "Sehr stark",
"Très épaisse": "Sehr dick",
"Vignettes": "Vignetten",
"Ajouter une galerie": "Eine Galerie hinzufügen",
"Éditer une galerie": "Eine Galerie bearbeiten",
"Effacer une galerie": "Eine Galerie löschen",
"Options des galeries": "Galerieoptionen",
"Thème des galeries": "Galeriethemen"
}

44
gallery/i18n/en_EN.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "Show the content of the page with the gallery",
"Alignement du bouton de retour": "Return button alignment",
"Alphabétique ": "Alphabetical",
"Alphabétique inverse": "Reverse alphabetics",
"Au-dessus": "Above",
"Aucune galerie": "No gallery",
"Bordure": "Border",
"Configuration de la galerie %s ": "Gallery settings %s ",
"Configuration des galeries": "Galleries's settings",
"Couleur de la bordure": "Border color",
"Couverture": "Cover",
"Discrète": "Discreet",
"Distribué avec marges": "Distributed with margins",
"Distribué sans marge": "Distributed without margin",
"Dossier cible": "Target file",
"En dessous": "Below",
"Epaisse": "Thick",
"Fine": "Fine",
"Forte": "Forte",
"Galerie ajoutée": "Gallery added",
"Galerie effacée": "Erased gallery",
"Légende": "Caption",
"Légendes": "Legends",
"Manuel": "Manuel",
"Marge": "Marge",
"Masqué": "Mask",
"Mode plein écran automatique": "Automatic full mode",
"Opacité au survol": "Opacity in overflight",
"Options de configuration": "Configuration options",
"Proportionnelle": "Proportional",
"Supprimer cette galerie ?": "Remove this gallery?",
"Tri des images": "Sorting images",
"Très Discrète": "Very discreet",
"Très fine": "Very fine",
"Très forte": "Very strong",
"Très épaisse": "Very thick",
"Vignettes": "Vignettes",
"Ajouter une galerie": "Add a gallery",
"Éditer une galerie": "Edit a gallery",
"Effacer une galerie": "Delete a gallery",
"Options des galeries": "Gallery options",
"Thème des galeries": "Gallery themes"
}

44
gallery/i18n/es.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "Mostrar contenido de página con galería",
"Alignement du bouton de retour": "Alineación del botón Atrás",
"Alphabétique ": "Alfabético",
"Alphabétique inverse": "Alfabético inverso",
"Au-dessus": "Encima",
"Aucune galerie": "Sin galería",
"Bordure": "Bordillo",
"Configuration de la galerie %s ": "Configuración de la galería %s ",
"Configuration des galeries": "Configuración de la galería",
"Couleur de la bordure": "Color del bordillo",
"Couverture": "Portada ",
"Discrète": "Discreto",
"Distribué avec marges": "Distribuido con márgenes",
"Distribué sans marge": "Distribuido sin margen",
"Dossier cible": "Carpeta de destino",
"En dessous": "Debajo",
"Epaisse": "grueso",
"Fine": "Fino",
"Forte": "Fuerte",
"Galerie ajoutée": "Galería añadida",
"Galerie effacée": "Galería eliminada",
"Légende": "Pie",
"Légendes": "Leyendas",
"Manuel": "Manual",
"Marge": "Margen",
"Masqué": "Máscara",
"Mode plein écran automatique": "Modo automático de pantalla completa",
"Opacité au survol": "Opacidad de desplazamiento",
"Options de configuration": "Opciones de configuración",
"Proportionnelle": "Proporcional",
"Supprimer cette galerie ?": "¿Borrar esta galería?",
"Tri des images": "Ordenar imágenes",
"Très Discrète": "Muy discreto",
"Très fine": "Muy fino",
"Très forte": "Muy fuerte",
"Très épaisse": "Muy grueso",
"Vignettes": "Viñetas",
"Ajouter une galerie": "Agregar una galería",
"Éditer une galerie": "Editar una galería",
"Effacer une galerie": "Borrar una galería",
"Options des galeries": "Opciones de galerías",
"Thème des galeries": "Temas de galerías"
}

44
gallery/i18n/fr_FR.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "",
"Alignement du bouton de retour": "",
"Alphabétique ": "",
"Alphabétique inverse": "",
"Au-dessus": "",
"Aucune galerie": "",
"Bordure": "",
"Configuration de la galerie %s ": "",
"Configuration des galeries": "",
"Couleur de la bordure": "",
"Couverture": "",
"Discrète": "",
"Distribué avec marges": "",
"Distribué sans marge": "",
"Dossier cible": "",
"En dessous": "",
"Epaisse": "",
"Fine": "",
"Forte": "",
"Galerie ajoutée": "",
"Galerie effacée": "",
"Légende": "",
"Légendes": "",
"Manuel": "",
"Marge": "",
"Masqué": "",
"Mode plein écran automatique": "",
"Opacité au survol": "",
"Options de configuration": "",
"Proportionnelle": "",
"Supprimer cette galerie ?": "",
"Tri des images": "",
"Très Discrète": "",
"Très fine": "",
"Très forte": "",
"Très épaisse": "",
"Vignettes": "",
"Ajouter une galerie": "",
"Éditer une galerie": "",
"Effacer une galerie": "",
"Options des galeries": "",
"Thème des galeries": ""
}

44
gallery/i18n/gr_GR.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "Εμφάνιση περιεχομένου σελίδας με γκαλερί",
"Alignement du bouton de retour": "Ευθυγράμμιση κουμπιού πίσω",
"Alphabétique ": "Αλφαβητική Ταξινόμηση",
"Alphabétique inverse": "Αντίστροφη αλφαβητική",
"Au-dessus": "Πάνω",
"Aucune galerie": "Δεν υπάρχει συλλογή εικόνων",
"Bordure": "κάδρο γύρω",
"Configuration de la galerie %s ": "Διαμόρφωση της γκαλερί %s ",
"Configuration des galeries": "Διαμόρφωση γκαλερί",
"Couleur de la bordure": "Χρώμα γραμμής",
"Couverture": "Κάλυψη ",
"Discrète": "διακριτικό",
"Distribué avec marges": "Διανομή με περιθώρια",
"Distribué sans marge": "Διανομή χωρίς περιθώριο",
"Dossier cible": "Φάκελος στόχος",
"En dessous": "Κάτω",
"Epaisse": "Παχιά γραμμή",
"Fine": "Λεπτή γραμμή ",
"Forte": "Ισχυρή αδιαφάνεια",
"Galerie ajoutée": "Προστέθηκε γκαλερί",
"Galerie effacée": "Γκαλερί διαγράφηκε",
"Légende": "Λεζάντα εικόνας",
"Légendes": "",
"Manuel": "Χειροκίνητη ",
"Marge": "Περιθώριο",
"Masqué": "κρυμμένο",
"Mode plein écran automatique": "Αυτόματη λειτουργία πλήρους οθόνης",
"Opacité au survol": "Αδιαφάνεια στο mouse-over",
"Options de configuration": "Επιλογές διαμόρφωσης",
"Proportionnelle": "Αναλογική",
"Supprimer cette galerie ?": "",
"Tri des images": "Ταξινόμηση εικόνων",
"Très Discrète": "Πολύ διακριτικό",
"Très fine": "Πολύ λεπτή γραμμή ",
"Très forte": "Πολύ Ισχυρή αδιαφάνεια",
"Très épaisse": "πολύ παχιά γραμμή",
"Vignettes": "",
"Ajouter une galerie": "Προσθήκη συλλογής",
"Éditer une galerie": "Επεξεργασία συλλογής",
"Effacer une galerie": "Διαγραφή συλλογής",
"Options des galeries": "Επιλογές συλλογών",
"Thème des galeries": "Θέματα συλλογών"
}

44
gallery/i18n/it.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "Mostra il contenuto della pagina con la galleria",
"Alignement du bouton de retour": "Allineamento del pulsante di ritorno",
"Alphabétique ": "Alfabetico",
"Alphabétique inverse": "Alfabetico inverso",
"Au-dessus": "Al di sopra",
"Aucune galerie": "Nessuna galleria",
"Bordure": "Bordo",
"Configuration de la galerie %s ": "Configurazione della galleria %s ",
"Configuration des galeries": "Configurazione di Galler",
"Couleur de la bordure": "Colore del bordo",
"Couverture": "Copertina",
"Discrète": "Discreto",
"Distribué avec marges": "Distribuito con margini",
"Distribué sans marge": "Distribuito senza margine",
"Dossier cible": "File di destinazione",
"En dessous": "Qui di seguito",
"Epaisse": "Spesso",
"Fine": "Bene",
"Forte": "Forte",
"Galerie ajoutée": "Galleria aggiunta",
"Galerie effacée": "Galleria cancellata",
"Légende": "Didascalia",
"Légendes": "Leggende",
"Manuel": "Manuel",
"Marge": "Marge",
"Masqué": "Maschera",
"Mode plein écran automatique": "Modalità completa automatica",
"Opacité au survol": "Opacità in luce eccessiva",
"Options de configuration": "Opzioni di configurazione",
"Proportionnelle": "Proporzionale",
"Supprimer cette galerie ?": "Rimuovere questa galleria?",
"Tri des images": "Ordinamento delle immagini",
"Très Discrète": "Molto discreto",
"Très fine": "Molto bene",
"Très forte": "Molto forte",
"Très épaisse": "Molto spesso",
"Vignettes": "Vignette",
"Ajouter une galerie": "Aggiungi una galleria",
"Éditer une galerie": "Modifica una galleria",
"Effacer une galerie": "Cancella una galleria",
"Options des galeries": "Opzioni delle gallerie",
"Thème des galeries": "Temi delle gallerie"
}

44
gallery/i18n/pt_PT.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "Mostre o conteúdo da página com a galeria",
"Alignement du bouton de retour": "Retornar alinhamento do botão",
"Alphabétique ": "Alfabético",
"Alphabétique inverse": "Alfabético reverso",
"Au-dessus": "Acima de",
"Aucune galerie": "Sem galeria",
"Bordure": "Fronteira",
"Configuration de la galerie %s ": "Configuração da galeria %s ",
"Configuration des galeries": "Configuração de Galler",
"Couleur de la bordure": "Cor da borda",
"Couverture": "Capa",
"Discrète": "Discreto",
"Distribué avec marges": "Distribuído com margens",
"Distribué sans marge": "Distribuído sem margem",
"Dossier cible": "Arquivo de destino",
"En dessous": "Abaixo de",
"Epaisse": "Espesso",
"Fine": "Multar",
"Forte": "Forte",
"Galerie ajoutée": "Galeria adicionada",
"Galerie effacée": "Galeria apagada",
"Légende": "Legenda",
"Légendes": "Legendas",
"Manuel": "Manuel",
"Marge": "Marge",
"Masqué": "mascarar",
"Mode plein écran automatique": "Modo completo automático",
"Opacité au survol": "Opacidade em Overflight",
"Options de configuration": "",
"Proportionnelle": "Proporcional",
"Supprimer cette galerie ?": "Remover esta galeria?",
"Tri des images": "Classificando imagens",
"Très Discrète": "Muito discreto",
"Très fine": "Muito bem",
"Très forte": "Muito forte",
"Très épaisse": "Muito espesso",
"Vignettes": "Vinhetas",
"Ajouter une galerie": "Adicionar uma galeria",
"Éditer une galerie": "Editar uma galeria",
"Effacer une galerie": "Apagar uma galeria",
"Options des galeries": "Opções de galerias",
"Thème des galeries": "Temas de galerias"
}

44
gallery/i18n/tr_TR.json Normal file
View File

@ -0,0 +1,44 @@
{
"Afficher le contenu de la page avec la galerie": "Sayfa içeriğini galeri ile görüntüle",
"Alignement du bouton de retour": "Geri düğmesi hizalaması",
"Alphabétique ": "Alfabetik",
"Alphabétique inverse": "Ters alfabetik",
"Au-dessus": "Üstünde",
"Aucune galerie": "Galeri Yok",
"Bordure": "Çerçeve",
"Configuration de la galerie %s ": "%s galeri yapılandırması",
"Configuration des galeries": "Galerilerin yapılandırması",
"Couleur de la bordure": "Çerçeve rengi",
"Couverture": "Kapak",
"Discrète": "Silik",
"Distribué avec marges": "Kenar boşluklarıyla dağıtıldı",
"Distribué sans marge": "Marjsız dağıtıldı",
"Dossier cible": "Hedef klasör",
"En dessous": "Altında",
"Epaisse": "Kalın",
"Fine": "İnce",
"Forte": "Güçlü",
"Galerie ajoutée": "Galeri eklendi",
"Galerie effacée": "Galeri silindi",
"Légende": "Başlık",
"Légendes": "Başlıklar",
"Manuel": "Manuel",
"Marge": "Kenar boşluğu",
"Masqué": "Gizli",
"Mode plein écran automatique": "Otomatik tam ekran modu",
"Opacité au survol": "Hover opaklığı",
"Options de configuration": "Yapılandırma seçenekleri",
"Proportionnelle": "Orantılı",
"Supprimer cette galerie ?": "Bu galeri silinsin mi?",
"Tri des images": "Resimleri sıralama",
"Très Discrète": "Çok silik",
"Très fine": "Çok ince",
"Très forte": "Çok güçlü",
"Très épaisse": "Çok kalın",
"Vignettes": "Küçük resim",
"Ajouter une galerie": "Galeri Ekle",
"Éditer une galerie": "Galeri Düzenle",
"Effacer une galerie": "Galeri Sil",
"Options des galeries": "Galeri Seçenekleri",
"Thème des galeries": "Galeri Temaları"
}

View File

@ -0,0 +1,12 @@
<?php $moduleData['gallery'] = [
'add' => $this->getInput('profilAddGalleryAdd', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilAddGalleryEdit', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilAddGalleryDelete', helper::FILTER_BOOLEAN),
'option' => $this->getInput('profilAddGalleryOption', helper::FILTER_BOOLEAN),
'theme' => $this->getInput('profilAddGalleryTheme', helper::FILTER_BOOLEAN),
'config' => $this->getInput('profilAddGalleryAdd', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddGalleryEdit', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddGalleryDelete', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddGalleryOption', helper::FILTER_BOOLEAN) ||
$this->getInput('profilAddGalleryTheme', helper::FILTER_BOOLEAN)
];

View File

@ -0,0 +1,12 @@
<?php $moduleData['gallery'] = [
'add' => $this->getInput('profilEditGalleryAdd', helper::FILTER_BOOLEAN),
'edit' => $this->getInput('profilEditGalleryEdit', helper::FILTER_BOOLEAN),
'delete' => $this->getInput('profilEditGalleryDelete', helper::FILTER_BOOLEAN),
'option' => $this->getInput('profilEditGalleryOption', helper::FILTER_BOOLEAN),
'theme' => $this->getInput('profilEditGalleryTheme', helper::FILTER_BOOLEAN),
'config' => $this->getInput('profilEditGalleryAdd', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditGalleryEdit', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditGalleryDelete', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditGalleryOption', helper::FILTER_BOOLEAN) ||
$this->getInput('profilEditGalleryTheme', helper::FILTER_BOOLEAN)
];

View File

@ -0,0 +1,28 @@
<div class="row">
<div class="col12">
<div class="block">
<h4>
<?php echo sprintf('%s %s', helper::translate('Permissions'), helper::translate('Galerie')); ?>
</h4>
<div class="row">
<div class="col4">
<?php echo template::checkbox('profilAddGalleryAdd', true, 'Ajouter une galerie'); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilAddGalleryEdit', true, 'Éditer une galerie'); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilAddGalleryDelete', true, 'Effacer une galerie'); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('profilAddGalleryOption', true, 'Options des galeries'); ?>
</div>
<div class="col6">
<?php echo template::checkbox('profilAddGalleryTheme', true, 'Thème des galeries'); ?>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,38 @@
<div class="row">
<div class="col12">
<div class="block">
<h4>
<?php echo sprintf('%s %s', helper::translate('Permissions'), helper::translate('Galerie')); ?>
</h4>
<div class="row">
<div class="col4">
<?php echo template::checkbox('profilEditGalleryAdd', true, 'Ajouter une galerie', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'gallery', 'add'])
]); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilEditGalleryEdit', true, 'Éditer une galerie', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'gallery', 'edit'])
]); ?>
</div>
<div class="col4">
<?php echo template::checkbox('profilEditGalleryDelete', true, 'Effacer une galerie', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'gallery', 'delete'])
]); ?>
</div>
</div>
<div class="row">
<div class="col6">
<?php echo template::checkbox('profilEditGalleryOption', true, 'Options des galeries', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'gallery', 'option'])
]); ?>
</div>
<div class="col6">
<?php echo template::checkbox('profilEditGalleryTheme', true, 'Thème des galeries', [
'checked' => $this->getData(['profil', $this->getUrl(2), $this->getUrl(3), 'gallery', 'theme'])
]); ?>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
<?php
class theme extends gallery {
public static $defaultTheme = [
'thumbAlign' => 'center',
'thumbWidth' => '18em',
'thumbHeight' => '15em',
'thumbMargin' => '.5em',
'thumbBorder' => '.1em',
'thumbOpacity' => '.7',
'thumbBorderColor' => 'rgba(221, 221, 221, 1)',
'thumbRadius' => '.3em',
'thumbShadows' => '1px 1px 10px',
'thumbShadowsColor' => 'rgba(125, 125, 125, 1)',
'legendHeight' => '.375em',
'legendAlign' => 'center',
'legendTextColor' => 'rgba(255, 255, 255, 1)',
'legendBgColor' => 'rgba(0, 0, 0, .6)'
];
public static $defaultData = [
"showUniqueGallery" => false,
"backPosition" => "top",
"backAlign" => "center",
'versionData' => '3.0'
];
}

View File

@ -0,0 +1,52 @@
.galleryPicture,
.galleryGalleryPicture {
display: block;
border: var(--thumbBorder) solid var(--thumbBorderColor);
height: var(--thumbHeight);
background-size: cover;
background-repeat: no-repeat;
background-position: center;
position: relative;
-webkit-transition: opacity .3s ease-out;
transition: opacity .3s ease-out;
border-radius: var(--thumbRadius);
box-shadow: var(--thumbShadows) var(--thumbShadowsColor);
-webkit-box-shadow: var(--thumbShadows) var(--thumbShadowsColor);
-moz-box-shadow: var(--thumbShadows) var(--thumbShadowsColor);
}
.galleryPicture:hover,
.galleryGalleryPicture:hover {
opacity: var(--thumbOpacity);
}
.galleryName,
.galleryGalleryName {
position: absolute;
left: 0;
right: 0;
bottom: 0;
border-radius: 0 0 calc(var(--thumbRadius)/2) calc(var(--thumbRadius)/2);
padding: var(--legendHeight);
background: var(--legendBgColor);
color: var(--legendTextColor);
text-align: var(--legendAlign);
}
.galleryRow {
display: flex;
flex-wrap: wrap;
justify-content: var(--thumbAlign);
}
.colPicture {
width : var(--thumbWidth);
max-width: 50%;
padding: var(--thumbMargin);
}
@media (max-width: 432px) {
.colPicture {
width: 90%;
max-width: 90%;
margin: 0.5em;
}
}

View File

@ -0,0 +1,27 @@
.galleryRow {
--thumbAlign: #thumbAlign#;
}
.colPicture {
--thumbWidth: #thumbWidth#;
--thumbMargin: #thumbMargin#;
}
.galleryPicture,
.galleryGalleryPicture {
--thumbHeight: #thumbHeight#;
--thumbBorder: #thumbBorder#;
--thumbBorderColor: #thumbBorderColor#;
--thumbRadius: #thumbRadius#;
--thumbShadows: #thumbShadows#;
--thumbShadowsColor: #thumbShadowsColor#;
}
.galleryName,
.galleryGalleryName {
--legendHeight: #legendHeight#;
--legendAlign: #legendAlign#;
--legendTextColor: #legendTextColor#;
--legendBgColor: #legendBgColor#;
}
.galleryPicture:hover,
.galleryGalleryPicture:hover {
--thumbOpacity: #thumbOpacity#;
}

22
gallery/vendor/tablednd/MIT-LICENSE.txt vendored Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) Denis Howlett <denish@isocra.com>
Copyright 2012 Nick Lombard - nickl- and other contributors
https://github.com/isocra/TableDnD
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More