From 68b4cab5ac5dc240a58fa9c80104776494bb1ac0 Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Thu, 11 Feb 2021 17:05:59 +0100 Subject: [PATCH 01/18] Initialisation du module Donwload --- module/download/download.php | 763 +++++++++++++ module/download/ressource/feed-icon-16.gif | Bin 0 -> 652 bytes module/download/vendor/FeedWriter/ATOM.php | 38 + module/download/vendor/FeedWriter/Feed.php | 1017 +++++++++++++++++ .../FeedWriter/InvalidOperationException.php | 33 + module/download/vendor/FeedWriter/Item.php | 413 +++++++ module/download/vendor/FeedWriter/README.md | 42 + module/download/vendor/FeedWriter/RSS1.php | 37 + module/download/vendor/FeedWriter/RSS2.php | 37 + module/download/view/add/add.css | 18 + module/download/view/add/add.js.php | 54 + module/download/view/add/add.php | 125 ++ module/download/view/comment/comment.css | 18 + module/download/view/comment/comment.js.php | 62 + module/download/view/comment/comment.php | 22 + module/download/view/config/config.css | 18 + module/download/view/config/config.js.php | 21 + module/download/view/config/config.php | 52 + module/download/view/edit/edit.css | 18 + module/download/view/edit/edit.js.php | 66 ++ module/download/view/edit/edit.php | 145 +++ module/download/view/index/index.css | 68 ++ module/download/view/index/index.php | 67 ++ module/download/view/item/item.css | 63 + module/download/view/item/item.js.php | 43 + module/download/view/item/item.php | 187 +++ module/download/view/rss/rss.php | 1 + 27 files changed, 3428 insertions(+) create mode 100644 module/download/download.php create mode 100644 module/download/ressource/feed-icon-16.gif create mode 100644 module/download/vendor/FeedWriter/ATOM.php create mode 100644 module/download/vendor/FeedWriter/Feed.php create mode 100644 module/download/vendor/FeedWriter/InvalidOperationException.php create mode 100644 module/download/vendor/FeedWriter/Item.php create mode 100644 module/download/vendor/FeedWriter/README.md create mode 100644 module/download/vendor/FeedWriter/RSS1.php create mode 100644 module/download/vendor/FeedWriter/RSS2.php create mode 100644 module/download/view/add/add.css create mode 100644 module/download/view/add/add.js.php create mode 100644 module/download/view/add/add.php create mode 100644 module/download/view/comment/comment.css create mode 100644 module/download/view/comment/comment.js.php create mode 100644 module/download/view/comment/comment.php create mode 100644 module/download/view/config/config.css create mode 100644 module/download/view/config/config.js.php create mode 100644 module/download/view/config/config.php create mode 100644 module/download/view/edit/edit.css create mode 100644 module/download/view/edit/edit.js.php create mode 100644 module/download/view/edit/edit.php create mode 100644 module/download/view/index/index.css create mode 100644 module/download/view/index/index.php create mode 100644 module/download/view/item/item.css create mode 100644 module/download/view/item/item.js.php create mode 100644 module/download/view/item/item.php create mode 100644 module/download/view/rss/rss.php diff --git a/module/download/download.php b/module/download/download.php new file mode 100644 index 00000000..8799daac --- /dev/null +++ b/module/download/download.php @@ -0,0 +1,763 @@ + + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +class download extends common { + + const EDIT_OWNER = 'owner'; + const EDIT_GROUP = 'group'; + const EDIT_ALL = 'all'; + + public static $actions = [ + 'add' => self::GROUP_MODERATOR, + 'comment' => self::GROUP_MODERATOR, + 'commentApprove' => self::GROUP_MODERATOR, + 'commentDelete' => self::GROUP_MODERATOR, + 'commentDeleteAll' => self::GROUP_MODERATOR, + 'config' => self::GROUP_MODERATOR, + 'delete' => self::GROUP_MODERATOR, + 'edit' => self::GROUP_MODERATOR, + 'index' => self::GROUP_VISITOR, + 'rss' => self::GROUP_VISITOR + ]; + + public static $items = []; + + // Signature de l'item + public static $itemSignature = ''; + + // 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 ', + ]; + + //Paramètre longueur maximale des commentaires en nb de caractères + public static $commentLength = [ + '500' => '500', + '1000' => '1000', + '2000' => '2000', + '5000' => '5000', + '10000' => '10000' + ]; + + // Permissions d'un item + public static $itemConsent = [ + self::EDIT_ALL => 'Tous les groupes', + self::EDIT_GROUP => 'Groupe du propriétaire', + self::EDIT_OWNER => 'Propriétaire' + ]; + + + public static $users = []; + + const VERSION = '4.2'; + const REALNAME = 'Téléchargement'; + + /** + * Flux RSS + */ + public function rss() { + // Inclure les classes + include_once 'module/news/vendor/FeedWriter/Item.php'; + include_once 'module/news/vendor/FeedWriter/Feed.php'; + include_once 'module/news/vendor/FeedWriter/RSS2.php'; + include_once 'module/news/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'])); + $feeds->setLink(helper::baseUrl() . $this->getUrl(0)); + $feeds->setDescription($this->getData (['page', $this->getUrl(0), 'metaDescription'])); + $feeds->setChannelElement('language', 'fr-FR'); + $feeds->setDate(date('r',time())); + $feeds->addGenerator(); + // Corps des items + $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC'); + $itemIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'items']), 'state', 'SORT_DESC'); + foreach($itemIdsPublishedOns as $itemId => $itemPublishedOn) { + if($itemPublishedOn <= time() AND $itemIdsStates[$itemId]) { + // Miniature + $parts = explode('/',$this->getData(['module', $this->getUrl(0), 'items', $itemId, 'picture'])); + $thumb = str_replace ($parts[(count($parts)-1)],'mini_' . $parts[(count($parts)-1)], $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'picture'])); + // Créer les items du flux + $newsitem = $feeds->createNewItem(); + // Signature de l'item + $author = $this->signature($this->getData(['module', $this->getUrl(0), 'items', $itemId, 'userId'])); + $newsitem->addElementArray([ + 'title' => $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'title']), + 'link' => helper::baseUrl() .$this->getUrl(0) . '/' . $itemId, + 'description' => '' . $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'title'])
+									 . '' . + $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'content']), + ]); + $newsitem->setAuthor($author,'no@mail.com'); + $newsitem->setId(helper::baseUrl() .$this->getUrl(0) . '/' . $itemId); + $newsitem->setDate(date('r', $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'publishedOn']))); + $imageData = getimagesize(helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb); + $newsitem->addEnclosure( helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb, + $imageData[0] * $imageData[1], + $imageData['mime'] + ); + $feeds->addItem($newsitem); + } + } + + // Valeurs en sortie + $this->addOutput([ + 'display' => self::DISPLAY_RSS, + 'content' => $feeds->generateFeed(), + 'view' => 'rss' + ]); + } + + /** + * Édition + */ + public function add() { + // Soumission du formulaire + if($this->isPost()) { + // Modification de l'userId + if($this->getUser('group') === self::GROUP_ADMIN){ + $newuserid = $this->getInput('downloadAddUserId', helper::FILTER_STRING_SHORT, true); + } + else{ + $newuserid = $this->getUser('id'); + } + // Incrémente l'id de l'item + $itemId = helper::increment($this->getInput('downloadAddTitle', helper::FILTER_ID), $this->getData(['page'])); + $itemId = helper::increment($itemId, (array) $this->getData(['module', $this->getUrl(0)])); + $itemId = helper::increment($itemId, array_keys(self::$actions)); + // Crée l'item + $this->setData(['module', + $this->getUrl(0), + 'items', + $itemId, [ + 'comment' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment']), + 'content' => $this->getInput('downloadAddContent', null), + 'picture' => $this->getInput('downloadAddPicture', helper::FILTER_STRING_SHORT, true), + 'file' => $this->getInput('downloadAddFile', helper::FILTER_STRING_SHORT, true), + 'fileVersion' => $this->getInput('downloadAddFileVersion', helper::FILTER_STRING_SHORT, true), + 'fileDate' => $this->getInput('downloadAddFileDate', helper::FILTER_DATETIME, true), + 'publishedOn' => $this->getInput('downloadAddPublishedOn', helper::FILTER_DATETIME, true), + 'state' => $this->getInput('downloadAddState', helper::FILTER_BOOLEAN), + 'title' => $this->getInput('downloadAddTitle', helper::FILTER_STRING_SHORT, true), + 'userId' => $newuserid, + 'editConsent' => $this->getInput('downloadAddConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('downloadAddConsent'), + 'commentMaxlength' => $this->getInput('downloadAddCommentMaxlength'), + 'commentApproved' => $this->getInput('downloadAddCommentApproved', helper::FILTER_BOOLEAN), + 'commentClose' => $this->getInput('downloadAddCommentClose', helper::FILTER_BOOLEAN), + 'commentNotification' => $this->getInput('downloadAddCommentNotification', helper::FILTER_BOOLEAN), + 'commentGroupNotification' => $this->getInput('downloadAddCommentGroupNotification', helper::FILTER_INT) + ] + ]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Nouvel item créé', + 'state' => true + ]); + } + // Liste des utilisateurs + self::$users = helper::arrayCollumn($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' => 'Nouvel item', + 'vendor' => [ + 'flatpickr', + 'tinymce' + ], + 'view' => 'add' + ]); + } + + /** + * Liste des commentaires + */ + public function comment() { + $comments = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2),'comment']); + self::$commentsDelete = template::button('downloadCommentDeleteAll', [ + 'class' => 'downloadCommentDeleteAll buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDeleteAll/' . $this->getUrl(2).'/' . $_SESSION['csrf'] , + 'ico' => 'cancel', + 'value' => 'Tout effacer' + ]); + // Ids des commentaires par ordre de création + $commentIds = array_keys(helper::arrayCollumn($comments, 'createdOn', 'SORT_DESC')); + // Pagination + $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['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), 'items', $this->getUrl(2),'commentApproved']) === true) { + $buttonApproval = template::button('downloadCommentApproved' . $commentIds[$i], [ + 'class' => $comment['approval'] === true ? 'downloadCommentRejected buttonGreen' : 'downloadCommentApproved buttonRed' , + 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentApprove/' . $this->getUrl(2) . '/' . $commentIds[$i] . '/' . $_SESSION['csrf'] , + 'value' => $comment['approval'] === true ? 'A' : 'R' + ]); + } + self::$comments[] = [ + mb_detect_encoding(strftime('%d %B %Y - %H:%M', $comment['createdOn']), 'UTF-8', true) + ? strftime('%d %B %Y - %H:%M', $comment['createdOn']) + : utf8_encode(strftime('%d %B %Y - %H:%M', $comment['createdOn'])), + $comment['content'], + $comment['userId'] ? $this->getData(['user', $comment['userId'], 'firstname']) . ' ' . $this->getData(['user', $comment['userId'], 'lastname']) : $comment['author'], + $buttonApproval, + template::button('downloadCommentDelete' . $commentIds[$i], [ + 'class' => 'downloadCommentDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDelete/' . $this->getUrl(2) . '/' . $commentIds[$i] . '/' . $_SESSION['csrf'] , + 'value' => template::ico('cancel') + ]) + ]; + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Gestion des commentaires : '. $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'title']), + 'view' => 'comment' + ]); + } + + /** + * Suppression de commentaire + */ + public function commentDelete() { + // Le commentaire n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(4) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Suppression + else { + $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment/'.$this->getUrl(2), + 'notification' => 'Commentaire supprimé', + 'state' => true + ]); + } + } + + /** + * Suppression de tous les commentaires de l'item $this->getUrl(2) + */ + public function commentDeleteAll() { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Suppression + else { + $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment',[] ]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment', + 'notification' => 'Commentaires supprimés', + 'state' => true + ]); + } + } + + /** + * Approbation oou désapprobation de commentaire + */ + public function commentApprove() { + // Le commentaire n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(4) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Inversion du statut + else { + $approved = !$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'approval']) ; + $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), [ + 'author' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'author']), + 'content' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'content']), + 'createdOn' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'createdOn']), + 'userId' => $this->getData(['module', $this->getUrl(0), 'items', $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 ? 'Commentaire approuvé' : 'Commentaire rejeté', + 'state' => $approved + ]); + } + } + + /** + * Configuration + */ + public function config() { + // Soumission du formulaire + if($this->isPost()) { + $this->setData(['module', $this->getUrl(0), 'config',[ + 'feeds' => $this->getInput('downloadConfigShowFeeds',helper::FILTER_BOOLEAN), + 'feedsLabel' => $this->getInput('downloadConfigFeedslabel',helper::FILTER_STRING_SHORT) + ]]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } else { + // Ids des items par ordre de publication + $itemIds = array_keys(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC')); + // Gestion des droits d'accès + $filterData=[]; + foreach ($itemIds as $key => $value) { + if ( + ( // Propriétaire + $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) === self::EDIT_OWNER + AND ( $this->getData(['module', $this->getUrl(0), 'items', $value,'userId']) === $this->getUser('id') + OR $this->getUser('group') === self::GROUP_ADMIN ) + ) + + OR ( + // Groupe + $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) !== self::EDIT_OWNER + AND $this->getUser('group') >= $this->getData(['module',$this->getUrl(0), 'items', $value,'editConsent']) + ) + OR ( + // Tout le monde + $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) === self::EDIT_ALL + ) + ) { + $filterData[] = $value; + } + } + $itemIds = $filterData; + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pages = $pagination['pages']; + // items en fonction de la pagination + for($i = $pagination['first']; $i < $pagination['last']; $i++) { + // Nombre de commentaires à approuver et approuvés + $approvals = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$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), 'items', $itemIds[$i],'comment'])); + } + // Met en forme le tableau + $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); + $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) + ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) + : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); + self::$items[] = [ + '' . + $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'title']) . + '', + $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileVersion']), + $date .' à '. $heure, + self::$states[$this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'state'])], + // Bouton pour afficher les commentaires de l'item + template::button('downloadConfigComment' . $itemIds[$i], [ + 'class' => ($toApprove || $approved ) > 0 ? 'buttonBlue' : 'buttonGrey' , + 'href' => ($toApprove || $approved ) > 0 ? helper::baseUrl() . $this->getUrl(0) . '/comment/' . $itemIds[$i] : '', + 'value' => $toApprove > 0 ? $toApprove . '/' . $approved : $approved + ]), + template::button('downloadConfigEdit' . $itemIds[$i], [ + 'href' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $itemIds[$i] . '/' . $_SESSION['csrf'], + 'value' => template::ico('pencil') + ]), + template::button('downloadConfigDelete' . $itemIds[$i], [ + 'class' => 'downloadConfigDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $itemIds[$i] . '/' . $_SESSION['csrf'], + 'value' => template::ico('cancel') + ]) + ]; + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Configuration du module', + 'view' => 'config' + ]); + } + } + + /** + * Suppression + */ + public function delete() { + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Suppression + else { + $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Item supprimé', + 'state' => true + ]); + } + } + + /** + * Édition + */ + public function edit() { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // L'item n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // L'item existe + else { + // Soumission du formulaire + if($this->isPost()) { + if($this->getUser('group') === self::GROUP_ADMIN){ + $newuserid = $this->getInput('downloadEditUserId', helper::FILTER_STRING_SHORT, true); + } + else{ + $newuserid = $this->getUser('id'); + } + $itemId = $this->getInput('downloadEditTitle', helper::FILTER_ID, true); + // Incrémente le nouvel id de l'item + if($itemId !== $this->getUrl(2)) { + $itemId = helper::increment($itemId, $this->getData(['page'])); + $itemId = helper::increment($itemId, $this->getData(['module', $this->getUrl(0),'items'])); + $itemId = helper::increment($itemId, array_keys(self::$actions)); + } + $this->setData(['module', + $this->getUrl(0), + 'items', + $itemId, [ + 'comment' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment']), + 'content' => $this->getInput('downloadEditContent', null), + 'picture' => $this->getInput('downloadEditPicture', helper::FILTER_STRING_SHORT, true), + 'file' => $this->getInput('downloadEditFile', helper::FILTER_STRING_SHORT, true), + 'fileVersion' => $this->getInput('downloadEditFileVersion', helper::FILTER_STRING_SHORT, true), + 'fileDate' => $this->getInput('downloadEditFileDate', helper::FILTER_DATETIME, true), + 'publishedOn' => $this->getInput('downloadEditPublishedOn', helper::FILTER_DATETIME, true), + 'state' => $this->getInput('downloadEditState', helper::FILTER_BOOLEAN), + 'title' => $this->getInput('downloadEditTitle', helper::FILTER_STRING_SHORT, true), + 'userId' => $newuserid, + 'editConsent' => $this->getInput('downloadEditConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('downloadEditConsent'), + 'commentMaxlength' => $this->getInput('downloadEditCommentMaxlength'), + 'commentApproved' => $this->getInput('downloadEditCommentApproved', helper::FILTER_BOOLEAN), + 'commentClose' => $this->getInput('downloadEditCommentClose', helper::FILTER_BOOLEAN), + 'commentNotification' => $this->getInput('downloadEditCommentNotification', helper::FILTER_BOOLEAN), + 'commentGroupNotification' => $this->getInput('downloadEditCommentGroupNotification', helper::FILTER_INT) + ] + ]); + // Supprime l'ancien item + if($itemId !== $this->getUrl(2)) { + $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]); + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } + // Liste des utilisateurs + self::$users = helper::arrayCollumn($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_MODERATOR) { + 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), 'items', $this->getUrl(2), 'title']), + 'vendor' => [ + 'flatpickr', + 'tinymce' + ], + 'view' => 'edit' + ]); + } + } + + /** + * Accueil (deux affichages en un pour éviter une url à rallonge) + */ + public function index() { + // Affichage d'un item + 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'item n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // L'item existe + else { + // Soumission du formulaire + if($this->isPost()) { + // Check la captcha + if( + $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD') + //AND $this->getInput('downloaditemcaptcha', helper::FILTER_INT) !== $this->getInput('downloaditemcaptchaFirstNumber', helper::FILTER_INT) + $this->getInput('downloaditemcaptchaSecondNumber', helper::FILTER_INT)) + AND password_verify($this->getInput('downloadItemCaptcha', helper::FILTER_INT), $this->getInput('downloadItemCaptchaResult') ) === false ) + { + self::$inputNotices['downloadItemCaptcha'] = 'Incorrect'; + } + // Crée le commentaire + $commentId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment'])); + $content = $this->getInput('downloadItemContent', false); + $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentId, [ + 'author' => $this->getInput('downloadItemAuthor', helper::FILTER_STRING_SHORT, empty($this->getInput('downloadItemUserId')) ? TRUE : FALSE), + 'content' => $content, + 'createdOn' => time(), + 'userId' => $this->getInput('downloadItemUserId'), + 'approval' => !$this->getData(['module', $this->getUrl(0), 'items', $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), 'items', $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), 'items', $this->getUrl(1), 'commentApproved']) === true ? 'Commentaire déposé en attente d\'approbation': 'Commentaire déposé'; + if ($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentNotification']) === true) { + $error = 0; + foreach($to as $key => $adress){ + $sent = $this->sendMail( + $adress, + 'Nouveau commentaire déposé', + 'Bonjour' . ' ' . $firstname[$key] . ' ' . $lastname[$key] . ',

' . + 'L\'item ' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'title']) . ' a reçu un nouveau commentaire.

', + '' + ); + if( $sent === false) $error++; + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl() . '#comment', + 'notification' => ($error === 0 ? $notification . '
Une notification a été envoyée.' : $notification . '
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), 'items', $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'item + self::$nbCommentsApproved = count($commentsApproved); + } + $commentIds = array_keys(helper::arrayCollumn($commentsApproved, 'createdOn', 'SORT_DESC')); + // Pagination + $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['config','itemsperPage']),'#comment'); + // Liste des pages + self::$pages = $pagination['pages']; + // Signature de l'item + self::$itemSignature = $this->signature($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'userId'])); + // Signature du commentaire édité + if($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')) { + 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), 'items', $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), 'items', $this->getUrl(1), 'comment', $commentIds[$i],'author']); + } + // Données du commentaire si approuvé + if ($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentIds[$i],'approval']) === true ) { + self::$comments[$commentIds[$i]] = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentIds[$i]]); + } + } + // Valeurs en sortie + $this->addOutput([ + 'showBarEditButton' => true, + 'title' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'title']), + 'vendor' => [ + 'tinymce' + ], + 'view' => 'item' + ]); + } + + } + // Liste des items + else { + // Ids des items par ordre de publication + $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'items']), 'publishedOn', 'SORT_DESC'); + $itemIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'state', 'SORT_DESC'); + $itemIds = []; + foreach($itemIdsPublishedOns as $itemId => $itemPublishedOn) { + if($itemPublishedOn <= time() AND $itemIdsStates[$itemId]) { + $itemIds[] = $itemId; + } + } + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pages = $pagination['pages']; + // Items en fonction de la pagination + for($i = $pagination['first']; $i < $pagination['last']; $i++) { + self::$items[$itemIds[$i]] = $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i]]); + } + // Valeurs en sortie + $this->addOutput([ + 'showBarEditButton' => true, + 'showPageContent' => true, + 'view' => 'index' + ]); + } + } + + /** + * Retourne la signature d'un utilisateur + */ + private function signature($userId) { + switch ($this->getData(['user', $userId, 'signature'])){ + case 1: + return $userId; + break; + case 2: + return $this->getData(['user', $userId, 'pseudo']); + break; + case 3: + return $this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']); + break; + case 4: + return $this->getData(['user', $userId, 'lastname']) . ' ' . $this->getData(['user', $userId, 'firstname']); + break; + default: + return $this->getData(['user', $userId, 'firstname']); + } + } +} + diff --git a/module/download/ressource/feed-icon-16.gif b/module/download/ressource/feed-icon-16.gif new file mode 100644 index 0000000000000000000000000000000000000000..26fa274454d016fdafa1b371e6d172030b61ac7d GIT binary patch literal 652 zcmZ?wbhEHb6krfwI9AB;Wun>ZdZS-+EPv0n`Z3e;&({zCe|~<~YVxAe@L9d_w<+eI zdQG2|=)Y_={m^OhuGRS8k57;D^!|N%b}LTnd4>L)F0+RfhHn~;9%Sjhsx^FEY4o_# z@aJ5|7tJR3Qg!}*e0DEi|9+vtokZ=wx3)ekF?ii+`l8kJ-}g^XOY|O<89r|`zE@!I zw8rQ~lgXb$Ew9>5@8;=0sWy6AYxJ_i?AMyOU+0$pSfBZBqR*FQ>F;Kx{5&!HWu4`h zy)%Byb9vwE_-=LQzc;sjE)9COY3j%A(|_+Qe^BcBc}e>FxhY@P6#u!o_50e)zxNOR z`}pkB;X?tj(k`Srkzulr~IeR}rieAnO4Zv4Ey|51V2uYJwG*T+4nasRbB>(}MY z-=|yroMriSlKIDO(^nhX|C0g~f3h%gG1N2YFaQB4P8irutD!|K07BTzlA_-oNcc1hVb)@I$C@&8w%Mnm^}*x>=-$uWM@XE3q3K( z+n30}qshBGH{_PjB8AT4CPx+_eKXyakCM*VXfpa8x!9!W!fBS3&{_Cd#ZQtY`q7^c z42zs*YqCAMQjx5}C&}jgh(nMu*=?1A*RH1rgcN%xD+z}kc=+HAgLSinO~;G`HX#{d V-YAuXg^H}50tyKne}y?1tN}38S(N|) literal 0 HcmV?d00001 diff --git a/module/download/vendor/FeedWriter/ATOM.php b/module/download/vendor/FeedWriter/ATOM.php new file mode 100644 index 00000000..28494501 --- /dev/null +++ b/module/download/vendor/FeedWriter/ATOM.php @@ -0,0 +1,38 @@ + + * + * 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 . + */ + +/** + * Wrapper for creating ATOM feeds + * + * @package UniversalFeedWriter + */ +class ATOM extends Feed +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct(Feed::ATOM); + } + +} diff --git a/module/download/vendor/FeedWriter/Feed.php b/module/download/vendor/FeedWriter/Feed.php new file mode 100644 index 00000000..d33a2059 --- /dev/null +++ b/module/download/vendor/FeedWriter/Feed.php @@ -0,0 +1,1017 @@ + + * Copyright (C) 2010-2016 Michael Bemmerl + * + * 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 . + */ + +/** + * Universal Feed Writer class + * + * Generate RSS 1.0, RSS2.0 and ATOM Feeds + * + * @package UniversalFeedWriter + * @author Anis uddin Ahmad + * @link http://www.ajaxray.com/projects/rss + */ +abstract class Feed +{ + // RSS 0.90 Officially obsoleted by 1.0 + // RSS 0.91, 0.92, 0.93 and 0.94 Officially obsoleted by 2.0 + // So, define constants for RSS 1.0, RSS 2.0 and ATOM + + const RSS1 = 'RSS 1.0'; + const RSS2 = 'RSS 2.0'; + const ATOM = 'ATOM'; + + const VERSION = '1.1.0'; + + /** + * Collection of all channel elements + */ + private $channels = array(); + + /** + * Collection of items as object of \FeedWriter\Item class. + */ + private $items = array(); + + /** + * Collection of other version wise data. + * + * Currently used to store the 'rdf:about' attribute and image element of the channel (both RSS1 only). + */ + private $data = array(); + + /** + * The tag names which have to encoded as CDATA + */ + private $CDATAEncoding = array(); + + /** + * Collection of XML namespaces + */ + private $namespaces = array(); + + /** + * Contains the format of this feed. + */ + private $version = null; + + /** + * Constructor + * + * If no version is given, a feed in RSS 2.0 format will be generated. + * + * @param string $version the version constant (RSS1/RSS2/ATOM). + */ + protected function __construct($version = Feed::RSS2) + { + $this->version = $version; + + // Setting default encoding + $this->encoding = 'utf-8'; + + // Setting default value for essential channel element + $this->setTitle($version . ' Feed'); + + // Add some default XML namespaces + $this->namespaces['content'] = 'http://purl.org/rss/1.0/modules/content/'; + $this->namespaces['wfw'] = 'http://wellformedweb.org/CommentAPI/'; + $this->namespaces['atom'] = 'http://www.w3.org/2005/Atom'; + $this->namespaces['rdf'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; + $this->namespaces['rss1'] = 'http://purl.org/rss/1.0/'; + $this->namespaces['dc'] = 'http://purl.org/dc/elements/1.1/'; + $this->namespaces['sy'] = 'http://purl.org/rss/1.0/modules/syndication/'; + + // Tag names to encode in CDATA + $this->addCDATAEncoding(array('description', 'content:encoded', 'summary')); + } + + // Start # public functions --------------------------------------------- + + /** + * Set the URLs for feed pagination. + * + * See RFC 5005, chapter 3. At least one page URL must be specified. + * + * @param string $nextURL The URL to the next page of this feed. Optional. + * @param string $previousURL The URL to the previous page of this feed. Optional. + * @param string $firstURL The URL to the first page of this feed. Optional. + * @param string $lastURL The URL to the last page of this feed. Optional. + * @link http://tools.ietf.org/html/rfc5005#section-3 + * @return self + * @throws \LogicException if none of the parameters are set. + */ + public function setPagination($nextURL = null, $previousURL = null, $firstURL = null, $lastURL = null) + { + if (empty($nextURL) && empty($previousURL) && empty($firstURL) && empty($lastURL)) + throw new \LogicException('At least one URL must be specified for pagination to work.'); + + if (!empty($nextURL)) + $this->setAtomLink($nextURL, 'next'); + + if (!empty($previousURL)) + $this->setAtomLink($previousURL, 'previous'); + + if (!empty($firstURL)) + $this->setAtomLink($firstURL, 'first'); + + if (!empty($lastURL)) + $this->setAtomLink($lastURL, 'last'); + + return $this; + } + + /** + * Add a channel element indicating the program used to generate the feed. + * + * @return self + * @throws InvalidOperationException if this method is called on an RSS1 feed. + */ + public function addGenerator() + { + if ($this->version == Feed::ATOM) + $this->setChannelElement('atom:generator', 'FeedWriter', array('uri' => 'https://github.com/mibe/FeedWriter')); + else if ($this->version == Feed::RSS2) + $this->setChannelElement('generator', 'FeedWriter'); + else + throw new InvalidOperationException('The generator element is not supported in RSS1 feeds.'); + + return $this; + } + + /** + * Add a XML namespace to the internal list of namespaces. After that, + * custom channel elements can be used properly to generate a valid feed. + * + * @access public + * @param string $prefix namespace prefix + * @param string $uri namespace name (URI) + * @return self + * @link http://www.w3.org/TR/REC-xml-names/ + * @throws \InvalidArgumentException if the prefix or uri is empty or NULL. + */ + public function addNamespace($prefix, $uri) + { + if (empty($prefix)) + throw new \InvalidArgumentException('The prefix may not be emtpy or NULL.'); + if (empty($uri)) + throw new \InvalidArgumentException('The uri may not be empty or NULL.'); + + $this->namespaces[$prefix] = $uri; + + return $this; + } + + /** + * Add a channel element to the feed. + * + * @access public + * @param string $elementName name of the channel tag + * @param string $content content of the channel tag + * @param array array of element attributes with attribute name as array key + * @param bool TRUE if this element can appear multiple times + * @return self + * @throws \InvalidArgumentException if the element name is not a string, empty or NULL. + */ + public function setChannelElement($elementName, $content, array $attributes = null, $multiple = 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.'); + + $entity['content'] = $content; + $entity['attributes'] = $attributes; + + if ($multiple === TRUE) + $this->channels[$elementName][] = $entity; + else + $this->channels[$elementName] = $entity; + + return $this; + } + + /** + * Set multiple channel elements from an array. Array elements + * should be 'channelName' => 'channelContent' format. + * + * @access public + * @param array array of channels + * @return self + */ + public function setChannelElementsFromArray(array $elementArray) + { + foreach ($elementArray as $elementName => $content) { + $this->setChannelElement($elementName, $content); + } + + return $this; + } + + /** + * Get the appropriate MIME type string for the current feed. + * + * @access public + * @return string The MIME type string. + */ + public function getMIMEType() + { + switch ($this->version) { + case Feed::RSS2 : $mimeType = "application/rss+xml"; + break; + case Feed::RSS1 : $mimeType = "application/rdf+xml"; + break; + case Feed::ATOM : $mimeType = "application/atom+xml"; + break; + default : $mimeType = "text/xml"; + } + + return $mimeType; + } + + /** + * Print the actual RSS/ATOM file + * + * Sets a Content-Type header and echoes the contents of the feed. + * Should only be used in situations where direct output is desired; + * if you need to pass a string around, use generateFeed() instead. + * + * @access public + * @param bool FALSE if the specific feed media type should be sent. + * @return void + * @throws \InvalidArgumentException if the useGenericContentType parameter is not boolean. + */ + public function printFeed($useGenericContentType = false) + { + if (!is_bool($useGenericContentType)) + throw new \InvalidArgumentException('The useGenericContentType parameter must be boolean.'); + + $contentType = "text/xml"; + + if (!$useGenericContentType) { + $contentType = $this->getMIMEType(); + } + + // Generate the feed before setting the header, so Exceptions will be nicely visible. + $feed = $this->generateFeed(); + header("Content-Type: " . $contentType . "; charset=" . $this->encoding); + echo $feed; + } + + /** + * Generate the feed. + * + * @access public + * @return string The complete feed XML. + * @throws InvalidOperationException if the link element of the feed is not set. + */ + public function generateFeed() + { + if ($this->version != Feed::ATOM && !array_key_exists('link', $this->channels)) + throw new InvalidOperationException('RSS1 & RSS2 feeds need a link element. Call the setLink method before this method.'); + + return $this->makeHeader() + . $this->makeChannels() + . $this->makeItems() + . $this->makeFooter(); + } + + /** + * Create a new Item. + * + * @access public + * @return Item instance of Item class + */ + public function createNewItem() + { + $Item = new Item($this->version); + + return $Item; + } + + /** + * Add one or more tags to the list of CDATA encoded tags + * + * @access public + * @param array $tags An array of tag names that are merged into the list of tags which should be encoded as CDATA + * @return self + */ + public function addCDATAEncoding(array $tags) + { + $this->CDATAEncoding = array_merge($this->CDATAEncoding, $tags); + + return $this; + } + + /** + * Get list of CDATA encoded properties + * + * @access public + * @return array Return an array of CDATA properties that are to be encoded as CDATA + */ + public function getCDATAEncoding() + { + return $this->CDATAEncoding; + } + + /** + * Remove tags from the list of CDATA encoded tags + * + * @access public + * @param array $tags An array of tag names that should be removed. + * @return void + */ + public function removeCDATAEncoding(array $tags) + { + // Call array_values to re-index the array. + $this->CDATAEncoding = array_values(array_diff($this->CDATAEncoding, $tags)); + } + + /** + * Add a FeedItem to the main class + * + * @access public + * @param Item $feedItem instance of Item class + * @return self + * @throws \InvalidArgumentException if the given item version mismatches. + */ + public function addItem(Item $feedItem) + { + if ($feedItem->getVersion() != $this->version) + { + $msg = sprintf('Feed type mismatch: This instance can handle %s feeds only, but item for %s feeds given.', $this->version, $feedItem->getVersion()); + throw new \InvalidArgumentException($msg); + } + + $this->items[] = $feedItem; + + return $this; + } + + // Wrapper functions ------------------------------------------------------------------- + + /** + * Set the 'encoding' attribute in the XML prolog. + * + * @access public + * @param string $encoding value of 'encoding' attribute + * @return self + * @throws \InvalidArgumentException if the encoding is not a string, empty or NULL. + */ + public function setEncoding($encoding) + { + if (empty($encoding)) + throw new \InvalidArgumentException('The encoding may not be empty or NULL.'); + if (!is_string($encoding)) + throw new \InvalidArgumentException('The encoding must be a string.'); + + $this->encoding = $encoding; + + return $this; + } + + /** + * Set the 'title' channel element + * + * @access public + * @param string $title value of 'title' channel tag + * @return self + */ + public function setTitle($title) + { + return $this->setChannelElement('title', $title); + } + + /** + * Set the date when the feed was lastly updated. + * + * This adds the 'updated' element to the feed. The value of the date parameter + * can be either an instance of the DateTime class, an integer containing a UNIX + * timestamp or a string which is parseable by PHP's 'strtotime' function. + * + * Not supported in RSS1 feeds. + * + * @access public + * @param DateTime|int|string Date which should be used. + * @return self + * @throws \InvalidArgumentException if the given date is not an instance of DateTime, a UNIX timestamp or a date string. + * @throws InvalidOperationException if this method is called on an RSS1 feed. + */ + public function setDate($date) + { + if ($this->version == Feed::RSS1) + throw new InvalidOperationException('The publication date is not supported in RSS1 feeds.'); + + // The feeds have different date formats. + $format = $this->version == Feed::ATOM ? \DATE_ATOM : \DATE_RSS; + + if ($date instanceof DateTime) + $date = $date->format($format); + else if(is_numeric($date) && $date >= 0) + $date = date($format, $date); + else if (is_string($date)) + { + $timestamp = strtotime($date); + if ($timestamp === FALSE) + throw new \InvalidArgumentException('The given date was not parseable.'); + + $date = date($format, $timestamp); + } + else + throw new \InvalidArgumentException('The given date is not an instance of DateTime, a UNIX timestamp or a date string.'); + + if ($this->version == Feed::ATOM) + $this->setChannelElement('updated', $date); + else + $this->setChannelElement('lastBuildDate', $date); + + return $this; + } + + /** + * Set a phrase or sentence describing the feed. + * + * @access public + * @param string $description Description of the feed. + * @return self + */ + public function setDescription($description) + { + if ($this->version != Feed::ATOM) + $this->setChannelElement('description', $description); + else + $this->setChannelElement('subtitle', $description); + + return $this; + } + + /** + * Set the 'link' channel element + * + * @access public + * @param string $link value of 'link' channel tag + * @return self + */ + public function setLink($link) + { + if ($this->version == Feed::ATOM) + $this->setAtomLink($link); + else + $this->setChannelElement('link', $link); + + return $this; + } + + /** + * Set custom 'link' channel elements. + * + * In ATOM feeds, only one link with alternate relation and the same combination of + * type and hreflang values. + * + * @access public + * @param string $href URI of this link + * @param string $rel relation type of the resource + * @param string $type MIME type of the target resource + * @param string $hreflang language of the resource + * @param string $title human-readable information about the resource + * @param int $length length of the resource in bytes + * @link https://www.iana.org/assignments/link-relations/link-relations.xml + * @link https://tools.ietf.org/html/rfc4287#section-4.2.7 + * @return self + * @throws \InvalidArgumentException on multiple occasions. + * @throws InvalidOperationException if the same link with the same attributes was already added to the feed. + */ + public function setAtomLink($href, $rel = null, $type = null, $hreflang = null, $title = null, $length = null) + { + $data = array('href' => $href); + + if ($rel != null) { + if (!is_string($rel) || empty($rel)) + throw new \InvalidArgumentException('rel parameter must be a string and a valid relation identifier.'); + + $data['rel'] = $rel; + } + if ($type != null) { + // 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.'); + + $data['type'] = $type; + } + if ($hreflang != null) { + // Regex used from RFC 4287, page 41 + if (!is_string($hreflang) || preg_match('/[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*/', $hreflang) != 1) + throw new \InvalidArgumentException('hreflang parameter must be a string and a valid language code.'); + + $data['hreflang'] = $hreflang; + } + if ($title != null) { + if (!is_string($title) || empty($title)) + throw new \InvalidArgumentException('title parameter must be a string and not empty.'); + + $data['title'] = $title; + } + if ($length != null) { + if (!is_int($length) || $length < 0) + throw new \InvalidArgumentException('length parameter must be a positive integer.'); + + $data['length'] = (string) $length; + } + + // ATOM spec. has some restrictions on atom:link usage + // See RFC 4287, page 12 (4.1.1) + if ($this->version == Feed::ATOM) { + foreach ($this->channels as $key => $value) { + if ($key != 'atom:link') + continue; + + // $value is an array , so check every element + foreach ($value as $linkItem) { + $attrib = $linkItem['attributes']; + // Only one link with relation alternate and same hreflang & type is allowed. + if (@$attrib['rel'] == 'alternate' && @$attrib['hreflang'] == $hreflang && @$attrib['type'] == $type) + throw new InvalidOperationException('The feed must not contain more than one link element with a' + . ' relation of "alternate" that has the same combination of type and hreflang attribute values.'); + } + } + } + + return $this->setChannelElement('atom:link', '', $data, true); + } + + /** + * Set an 'atom:link' channel element with relation=self attribute. + * Needs the full URL to this feed. + * + * @link http://www.rssboard.org/rss-profile#namespace-elements-atom-link + * @access public + * @param string $url URL to this feed + * @return self + */ + public function setSelfLink($url) + { + return $this->setAtomLink($url, 'self', $this->getMIMEType()); + } + + /** + * Set the 'image' channel element + * + * @access public + * @param string $url URL of the image + * @param string $title Title of the image. RSS only. + * @param string $link Link target URL of the image. RSS only. + * @return self + * @throws \InvalidArgumentException if the url is invalid. + * @throws \InvalidArgumentException if the title and link parameter are not a string or empty. + */ + public function setImage($url, $title = null, $link = null) + { + if (!is_string($url) || empty($url)) + throw new \InvalidArgumentException('url parameter must be a string and may not be empty or NULL.'); + + // RSS feeds have support for a title & link element. + if ($this->version != Feed::ATOM) + { + if (!is_string($title) || empty($title)) + throw new \InvalidArgumentException('title parameter must be a string and may not be empty or NULL.'); + if (!is_string($link) || empty($link)) + throw new \InvalidArgumentException('link parameter must be a string and may not be empty or NULL.'); + + $data = array('title'=>$title, 'link'=>$link, 'url'=>$url); + $name = 'image'; + } + else + { + $name = 'logo'; + $data = $url; + } + + // Special handling for RSS1 again (since RSS1 is a bit strange...) + if ($this->version == Feed::RSS1) + { + $this->data['Image'] = $data; + return $this->setChannelElement($name, '', array('rdf:resource' => $url), false); + } + else + return $this->setChannelElement($name, $data); + } + + /** + * Set the channel 'rdf:about' attribute, which is used in RSS1 feeds only. + * + * @access public + * @param string $url value of 'rdf:about' attribute of the channel element + * @return self + * @throws InvalidOperationException if this method is called and the feed is not of type RSS1. + * @throws \InvalidArgumentException if the given URL is invalid. + */ + public function setChannelAbout($url) + { + if ($this->version != Feed::RSS1) + throw new InvalidOperationException("This method is only supported in RSS1 feeds."); + if (empty($url)) + throw new \InvalidArgumentException('The about URL may not be empty or NULL.'); + if (!is_string($url)) + throw new \InvalidArgumentException('The about URL must be a string.'); + + $this->data['ChannelAbout'] = $url; + + return $this; + } + + /** + * Generate an UUID. + * + * The UUID is based on an MD5 hash. If no key is given, a unique ID as the input + * for the MD5 hash is generated. + * + * @author Anis uddin Ahmad + * @access public + * @param string $key optional key on which the UUID is generated + * @param string $prefix an optional prefix + * @return string the formatted UUID + */ + public static function uuid($key = null, $prefix = '') + { + $key = ($key == null) ? uniqid(rand()) : $key; + $chars = md5($key); + $uuid = substr($chars,0,8) . '-'; + $uuid .= substr($chars,8,4) . '-'; + $uuid .= substr($chars,12,4) . '-'; + $uuid .= substr($chars,16,4) . '-'; + $uuid .= substr($chars,20,12); + + return $prefix . $uuid; + } + + /** + * Replace invalid XML characters. + * + * @link http://www.phpwact.org/php/i18n/charsets#xml See utf8_for_xml() function + * @link http://www.w3.org/TR/REC-xml/#charsets + * @link https://github.com/mibe/FeedWriter/issues/30 + * + * @access public + * @param string $string string which should be filtered + * @param string $replacement replace invalid characters with this string + * @return string the filtered string + */ + public static function filterInvalidXMLChars($string, $replacement = '_') // default to '\x{FFFD}' ??? + { + $result = preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u', $replacement, $string); + + // Did the PCRE replace failed because of bad UTF-8 data? + // If yes, try a non-multibyte regex and without the UTF-8 mode enabled. + if ($result == NULL && preg_last_error() == PREG_BAD_UTF8_ERROR) + $result = preg_replace('/[^\x09\x0a\x0d\x20-\xFF]+/', $replacement, $string); + + // In case the regex replacing failed completely, return the whole unfiltered string. + if ($result == NULL) + $result = $string; + + return $result; + } + // End # public functions ---------------------------------------------- + + // Start # private functions ---------------------------------------------- + + /** + * Returns all used XML namespace prefixes in this instance. + * This includes all channel elements and feed items. + * Unfortunately some namespace prefixes are not included, + * because they are hardcoded, e.g. rdf. + * + * @access private + * @return array Array with namespace prefix as value. + */ + private function getNamespacePrefixes() + { + $prefixes = array(); + + // Get all tag names from channel elements... + $tags = array_keys($this->channels); + + // ... and now all names from feed items + foreach ($this->items as $item) { + foreach (array_keys($item->getElements()) as $key) { + if (!in_array($key, $tags)) { + $tags[] = $key; + } + } + } + + // Look for prefixes in those tag names + foreach ($tags as $tag) { + $elements = explode(':', $tag); + + if (count($elements) != 2) + continue; + + $prefixes[] = $elements[0]; + } + + return array_unique($prefixes); + } + + /** + * Returns the XML header and root element, depending on the feed type. + * + * @access private + * @return string The XML header of the feed. + * @throws InvalidOperationException if an unknown XML namespace prefix is encountered. + */ + private function makeHeader() + { + $out = 'encoding.'" ?>' . PHP_EOL; + + $prefixes = $this->getNamespacePrefixes(); + $attributes = array(); + $tagName = ''; + $defaultNamespace = ''; + + if ($this->version == Feed::RSS2) { + $tagName = 'rss'; + $attributes['version'] = '2.0'; + } elseif ($this->version == Feed::RSS1) { + $tagName = 'rdf:RDF'; + $prefixes[] = 'rdf'; + $defaultNamespace = $this->namespaces['rss1']; + } elseif ($this->version == Feed::ATOM) { + $tagName = 'feed'; + $defaultNamespace = $this->namespaces['atom']; + + // Ugly hack to remove the 'atom' value from the prefixes array. + $prefixes = array_flip($prefixes); + unset($prefixes['atom']); + $prefixes = array_flip($prefixes); + } + + // Iterate through every namespace prefix and add it to the element attributes. + foreach ($prefixes as $prefix) { + if (!isset($this->namespaces[$prefix])) + throw new InvalidOperationException('Unknown XML namespace prefix: \'' . $prefix . '\'.' + . ' Use the addNamespace method to add support for this prefix.'); + else + $attributes['xmlns:' . $prefix] = $this->namespaces[$prefix]; + } + + // Include default namepsace, if required + if (!empty($defaultNamespace)) + $attributes['xmlns'] = $defaultNamespace; + + $out .= $this->makeNode($tagName, '', $attributes, true); + + return $out; + } + + /** + * Closes the open tags at the end of file + * + * @access private + * @return string The XML footer of the feed. + */ + private function makeFooter() + { + if ($this->version == Feed::RSS2) { + return '' . PHP_EOL . ''; + } elseif ($this->version == Feed::RSS1) { + return ''; + } elseif ($this->version == Feed::ATOM) { + return ''; + } + } + + /** + * Creates a single node in XML format + * + * @access private + * @param string $tagName name of the tag + * @param mixed $tagContent tag value as string or array of nested tags in 'tagName' => 'tagValue' format + * @param array $attributes Attributes (if any) in 'attrName' => 'attrValue' format + * @param bool $omitEndTag True if the end tag should be omitted. Defaults to false. + * @return string formatted xml tag + * @throws \InvalidArgumentException if the tagContent is not an array and not a string. + */ + private function makeNode($tagName, $tagContent, array $attributes = null, $omitEndTag = false) + { + $nodeText = ''; + $attrText = ''; + + if ($attributes != null) { + foreach ($attributes as $key => $value) { + $value = self::filterInvalidXMLChars($value); + $value = htmlspecialchars($value); + $attrText .= " $key=\"$value\""; + } + } + + $attrText .= (in_array($tagName, $this->CDATAEncoding) && $this->version == Feed::ATOM) ? ' type="html"' : ''; + $nodeText .= "<{$tagName}{$attrText}>"; + $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? ' $value) { + if (is_array($value)) { + $nodeText .= PHP_EOL; + foreach ($value as $subValue) { + $nodeText .= $this->makeNode($key, $subValue); + } + } else if (is_string($value)) { + $nodeText .= $this->makeNode($key, $value); + } else { + throw new \InvalidArgumentException("Unknown node-value type for $key"); + } + } + } else { + $tagContent = self::filterInvalidXMLChars($tagContent); + $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? $this->sanitizeCDATA($tagContent) : htmlspecialchars($tagContent); + } + + $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? ']]>' : ''; + + if (!$omitEndTag) + $nodeText .= ""; + + $nodeText .= PHP_EOL; + + return $nodeText; + } + + /** + * Make the channels. + * + * @access private + * @return string The feed header as XML containing all the feed metadata. + */ + private function makeChannels() + { + $out = ''; + + //Start channel tag + switch ($this->version) { + case Feed::RSS2: + $out .= '' . PHP_EOL; + break; + case Feed::RSS1: + $out .= (isset($this->data['ChannelAbout']))? "data['ChannelAbout']}\">" : "channels['link']['content']}\">"; + break; + } + + //Print Items of channel + foreach ($this->channels as $key => $value) { + // In ATOM feeds, strip all ATOM namespace prefixes from the tag name. They are not needed here, + // because the ATOM namespace name is set as default namespace. + if ($this->version == Feed::ATOM && strncmp($key, 'atom', 4) == 0) { + $key = substr($key, 5); + } + + // The channel element can occur multiple times, when the key 'content' is not in the array. + if (!array_key_exists('content', $value)) { + // If this is the case, iterate through the array with the multiple elements. + foreach ($value as $singleElement) { + $out .= $this->makeNode($key, $singleElement['content'], $singleElement['attributes']); + } + } else { + $out .= $this->makeNode($key, $value['content'], $value['attributes']); + } + } + + if ($this->version == Feed::RSS1) { + //RSS 1.0 have special tag with channel + $out .= "" . PHP_EOL . "" . PHP_EOL; + foreach ($this->items as $item) { + $thisItems = $item->getElements(); + $out .= "" . PHP_EOL; + } + $out .= "" . PHP_EOL . "" . PHP_EOL . "" . PHP_EOL; + + // An image has its own element after the channel elements. + if (array_key_exists('image', $this->data)) + $out .= $this->makeNode('image', $this->data['Image'], array('rdf:about' => $this->data['Image']['url'])); + } else if ($this->version == Feed::ATOM) { + // ATOM feeds have a unique feed ID. Use the title channel element as key. + $out .= $this->makeNode('id', Feed::uuid($this->channels['title']['content'], 'urn:uuid:')); + } + + return $out; + } + + /** + * Prints formatted feed items + * + * @access private + * @return string The XML of every feed item. + */ + private function makeItems() + { + $out = ''; + + foreach ($this->items as $item) { + $thisItems = $item->getElements(); + + // The argument is printed as rdf:about attribute of item in RSS 1.0 + // We're using the link set in the item (which is mandatory) as the about attribute. + if ($this->version == Feed::RSS1) + $out .= $this->startItem($thisItems['link']['content']); + else + $out .= $this->startItem(); + + foreach ($thisItems as $feedItem) { + $name = $feedItem['name']; + + // Strip all ATOM namespace prefixes from tags when feed is an ATOM feed. + // Not needed here, because the ATOM namespace name is used as default namespace. + if ($this->version == Feed::ATOM && strncmp($name, 'atom', 4) == 0) + $name = substr($name, 5); + + $out .= $this->makeNode($name, $feedItem['content'], $feedItem['attributes']); + } + $out .= $this->endItem(); + } + + return $out; + } + + /** + * Make the starting tag of channels + * + * @access private + * @param string $about The value of about attribute which is used for RSS 1.0 only. + * @return string The starting XML tag of an feed item. + * @throws InvalidOperationException if this object misses the data for the about attribute. + */ + private function startItem($about = false) + { + $out = ''; + + if ($this->version == Feed::RSS2) { + $out .= '
' . PHP_EOL; + } elseif ($this->version == Feed::RSS1) { + if ($about) { + $out .= "" . PHP_EOL; + } else { + throw new InvalidOperationException("Missing data for about attribute. Call setChannelAbout method."); + } + } elseif ($this->version == Feed::ATOM) { + $out .= "" . PHP_EOL; + } + + return $out; + } + + /** + * Closes feed item tag + * + * @access private + * @return string The ending XML tag of an feed item. + */ + private function endItem() + { + if ($this->version == Feed::RSS2 || $this->version == Feed::RSS1) { + return '
' . PHP_EOL; + } elseif ($this->version == Feed::ATOM) { + return '' . PHP_EOL; + } + } + + /** + * Sanitizes data which will be later on returned as CDATA in the feed. + * + * A "]]>" respectively "", "]]>", $text); + $text = str_replace(" + * + * 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 . + */ + +/** + * The exception that is thrown when an invalid operation is performed on + * the object. + * + * @package UniversalFeedWriter + */ +class InvalidOperationException extends LogicException +{ +} diff --git a/module/download/vendor/FeedWriter/Item.php b/module/download/vendor/FeedWriter/Item.php new file mode 100644 index 00000000..c4f21a93 --- /dev/null +++ b/module/download/vendor/FeedWriter/Item.php @@ -0,0 +1,413 @@ + + * Copyright (C) 2010-2013, 2015-2016 Michael Bemmerl + * + * 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 . + */ + +/** + * Universal Feed Writer + * + * Item class - Used as feed element in Feed class + * + * @package UniversalFeedWriter + * @author Anis uddin Ahmad + * @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 download 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 an instance of the + * DateTime class, an integer containing a UNIX timestamp or a string + * which is parseable by PHP's 'strtotime' function. + * + * @access public + * @param DateTime|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 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 4288 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/rfc4288 + * @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', 'cap', 'cid', 'coap', 'coaps', 'crid', 'data', 'dav', 'dict', 'dns', 'example', 'fax', 'file', 'filesystem', 'ftp', 'geo', 'go', 'gopher', 'h323', 'http', 'https', 'iax', 'icap', 'im', 'imap', 'info', 'ipp', 'ipps', 'iris', 'iris.beep', 'iris.lwz', 'iris.xpc', 'iris.xpcs', 'jabber', 'ldap', 'mailserver', 'mailto', 'mid', 'modem', 'msrp', 'msrps', 'mtqp', 'mupdate', 'news', 'nfs', 'ni', 'nih', 'nntp', 'opaquelocktoken', 'pack', 'pkcs11', 'pop', 'pres', 'prospero', 'reload', 'rtsp', 'rtsps', 'rtspu', 'service', 'session', 'shttp', 'sieve', 'sip', 'sips', 'sms', 'snews', 'snmp', 'soap.beep', 'soap.beeps', 'stun', 'stuns', 'tag', 'tel', 'telnet', 'tftp', 'thismessage', 'tip', 'tn3270', 'turn', 'turns', 'tv', 'urn', 'vemmi', 'videotex', 'vnc', 'wais', '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 diff --git a/module/download/vendor/FeedWriter/README.md b/module/download/vendor/FeedWriter/README.md new file mode 100644 index 00000000..f630af99 --- /dev/null +++ b/module/download/vendor/FeedWriter/README.md @@ -0,0 +1,42 @@ +# 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. + +## 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 diff --git a/module/download/vendor/FeedWriter/RSS1.php b/module/download/vendor/FeedWriter/RSS1.php new file mode 100644 index 00000000..a0465cf5 --- /dev/null +++ b/module/download/vendor/FeedWriter/RSS1.php @@ -0,0 +1,37 @@ + + * + * 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 . + */ + +/** + * Wrapper for creating RSS1 feeds + * + * @package UniversalFeedWriter + */ +class RSS1 extends Feed +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct(Feed::RSS1); + } +} diff --git a/module/download/vendor/FeedWriter/RSS2.php b/module/download/vendor/FeedWriter/RSS2.php new file mode 100644 index 00000000..9e36a728 --- /dev/null +++ b/module/download/vendor/FeedWriter/RSS2.php @@ -0,0 +1,37 @@ + + * + * 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 . + */ + +/** + * Wrapper for creating RSS2 feeds + * + * @package UniversalFeedWriter + */ +class RSS2 extends Feed +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct(Feed::RSS2); + } +} diff --git a/module/download/view/add/add.css b/module/download/view/add/add.css new file mode 100644 index 00000000..805e9150 --- /dev/null +++ b/module/download/view/add/add.css @@ -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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/module/download/view/add/add.js.php b/module/download/view/add/add.js.php new file mode 100644 index 00000000..d88ff563 --- /dev/null +++ b/module/download/view/add/add.js.php @@ -0,0 +1,54 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +/** + * Soumission du formulaire pour enregistrer en brouillon + */ +$("#downloadAddDraft").on("click", function() { + $("#downloadAddState").val(0); + $("#downloadAddForm").trigger("submit"); +}); + +/** + * Options de commentaires + */ +$("#downloadAddCommentClose").on("change", function() { + if ($(this).is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } +}); + +$("#downloadAddCommentNotification").on("change", function() { + if ($(this).is(':checked') ) { + $("#downloadAddCommentGroupNotification").slideDown(); + } else { + $("#downloadAddCommentGroupNotification").slideUp(); + } +}); + + +$( document).ready(function() { + + if ($("#downloadAddCloseComment").is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } + + if ($("#downloadAddCommentNotification").is(':checked') ) { + $("#downloadAddCommentGroupNotification").slideDown(); + } else { + $("#downloadAddCommentGroupNotification").slideUp(); + } +}); \ No newline at end of file diff --git a/module/download/view/add/add.php b/module/download/view/add/add.php new file mode 100644 index 00000000..e75215af --- /dev/null +++ b/module/download/view/add/add.php @@ -0,0 +1,125 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ true, + 'value' => 'Enregistrer en brouillon' + ]); ?> + true + ]); ?> +
+
+ 'Publier' + ]); ?> +
+
+
+
+
+

Informations générales

+
+
+ 'Titre' + ]); ?> +
+
+ 'Version' + ]); ?> +
+
+ 'Date' + ]); ?> +
+
+
+
+ 'Archive du fichier' + ]); ?> +
+
+ 'Capture d\'écran', + 'type' => 1 + ]); ?> +
+
+
+
+
+ 'editorWysiwyg' + ]); ?> +
+
+
+

Options de publication

+
+
+ 'Auteur', + 'selected' => $this->getUser('id'), + 'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false + ]); ?> +
+
+ 'L\'item n\'est visible qu\'après la date de publication prévue.', + 'label' => 'Date de publication', + 'value' => time() + ]); ?> +
+
+ 'Edition / Suppression', + 'selected' => $module::EDIT_ALL, + 'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'item sans restriction' + ]); ?> +
+
+
+
+
+
+
+
+

Commentaires

+
+
+ +
+
+ +
+
+ 'Choix du nombre maximum de caractères pour chaque commentaire de l\'item, mise en forme html comprise.', + 'label' => 'Caractères par commentaire' + ]); ?> +
+
+
+
+ +
+
+ +
+
+
+
+
+ diff --git a/module/download/view/comment/comment.css b/module/download/view/comment/comment.css new file mode 100644 index 00000000..805e9150 --- /dev/null +++ b/module/download/view/comment/comment.css @@ -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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/module/download/view/comment/comment.js.php b/module/download/view/comment/comment.js.php new file mode 100644 index 00000000..fe2f99d5 --- /dev/null +++ b/module/download/view/comment/comment.js.php @@ -0,0 +1,62 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** + * Confirmation de suppression + */ +$(".downloadCommentDelete").on("click", function() { + var _this = $(this); + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + return core.confirm("Supprimer le commentaire de l'item " + nom + " ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation d'approbation + */ +$(".downloadCommentApproved").on("click", function() { + var _this = $(this); + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + return core.confirm("Approuver le commentaire de l'item " + nom + " ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation de rejet + */ +$(".downloadCommentRejected").on("click", function() { + var _this = $(this); + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + return core.confirm("Rejeter le commentaire de l'item " + nom + " ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation de suppression en masse + */ +$(".downloadCommentDeleteAll").on("click", function() { + var _this = $(this); + var nombre = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'comment' ])); ?>"; + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + if( nombre === "1"){ + var message = "Supprimer le commentaire de l'item " + nom + " ?"; + } else{ + var message = "Supprimer les " + nombre + " commentaires de l'item " + nom + " ?"; + } + return core.confirm(message, function() { + $(location).attr("href", _this.attr("href")); + }); +}); diff --git a/module/download/view/comment/comment.php b/module/download/view/comment/comment.php new file mode 100644 index 00000000..76c50a61 --- /dev/null +++ b/module/download/view/comment/comment.php @@ -0,0 +1,22 @@ +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+ + +
+ +
+ +
+ + '; ?> + + + + diff --git a/module/download/view/config/config.css b/module/download/view/config/config.css new file mode 100644 index 00000000..805e9150 --- /dev/null +++ b/module/download/view/config/config.css @@ -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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/module/download/view/config/config.js.php b/module/download/view/config/config.js.php new file mode 100644 index 00000000..bc462697 --- /dev/null +++ b/module/download/view/config/config.js.php @@ -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 Rémi Jean + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +/** + * Confirmation de suppression + */ +$(".downloadConfigDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer cet item ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); \ No newline at end of file diff --git a/module/download/view/config/config.php b/module/download/view/config/config.php new file mode 100644 index 00000000..1a3b42c3 --- /dev/null +++ b/module/download/view/config/config.php @@ -0,0 +1,52 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0), 'items', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ helper::baseUrl() . $this->getUrl(0) . '/add', + 'ico' => 'plus', + 'value' => 'Item' + ]); ?> +
+
+ +
+
+
+
+
+

Paramètres du module

+
+
+ $this->getData(['module', $this->getUrl(0), 'config', 'feeds']), + ]); ?> +
+
+ 'Etiquette du flux', + 'value' => $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel']) + ]); ?> +
+
+
+
+
+ + + + + + + +
Version n° + +
+ diff --git a/module/download/view/edit/edit.css b/module/download/view/edit/edit.css new file mode 100644 index 00000000..805e9150 --- /dev/null +++ b/module/download/view/edit/edit.css @@ -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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/module/download/view/edit/edit.js.php b/module/download/view/edit/edit.js.php new file mode 100644 index 00000000..148e937a --- /dev/null +++ b/module/download/view/edit/edit.js.php @@ -0,0 +1,66 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +// Lien de connexion +$("#downloadEditMailNotification").on("change", function() { + if($(this).is(":checked")) { + $("#formConfigGroup").show(); + } + else { + $("#formConfigGroup").hide(); + } +}).trigger("change"); + + +/** + * Soumission du formulaire pour enregistrer en brouillon + */ +$("#downloadEditDraft").on("click", function() { + $("#downloadEditState").val(0); + $("#downloadEditForm").trigger("submit"); +}); + +/** + * Options de commentaires + */ +$("#downloadEditCommentClose").on("change", function() { + if ($(this).is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } +}); + +$("#downloadEditCommentNotification").on("change", function() { + if ($(this).is(':checked') ) { + $("#downloadEditCommentGroupNotification").slideDown(); + } else { + $("#downloadEditCommentGroupNotification").slideUp(); + } +}); + + +$( document).ready(function() { + + if ($("#downloadEditCloseComment").is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } + + if ($("#downloadEditCommentNotification").is(':checked') ) { + $("#downloadEditCommentGroupNotification").slideDown(); + } else { + $("#downloadEditCommentGroupNotification").slideUp(); + } +}); \ No newline at end of file diff --git a/module/download/view/edit/edit.php b/module/download/view/edit/edit.php new file mode 100644 index 00000000..1a395485 --- /dev/null +++ b/module/download/view/edit/edit.php @@ -0,0 +1,145 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ true, + 'value' => 'Enregistrer en brouillon' + ]); ?> + true + ]); ?> +
+
+ 'Publier' + ]); ?> + +
+
+
+
+
+

Informations générales

+ +
+
+ 'Titre', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'title']) + ]); ?> +
+
+ 'Version', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileVersion']) + ]); ?> +
+
+ 'Date', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileDate']) + ]); ?> +
+
+
+
+ 'Archive du fichier', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']) + ]); ?> +
+
+ 'Capture d\'écran', + 'type' => 1, + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'picture']) + ]); ?> +
+
+
+
+
+ 'editorWysiwyg', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'content']) + ]); ?> +
+
+
+

Options de publication

+
+
+ 'Auteur', + 'selected' => $this->getUser('id'), + 'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false + ]); ?> +
+
+ 'L\'item n\'est visible qu\'après la date de publication prévue.', + 'label' => 'Date de publication', + 'value' => time() + ]); ?> +
+
+ 'Edition / Suppression', + 'selected' => is_numeric($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'editConsent'])) ? $module::EDIT_GROUP : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'editConsent']), + 'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'item sans restriction' + ]); ?> +
+
+
+
+
+
+
+
+

Commentaires

+
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentClose']) + ]); ?> +
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentApproved']), + '' + ]); ?> +
+
+ 'Choix du nombre maximum de caractères pour chaque commentaire de l\'item, mise en forme html comprise.', + 'label' => 'Caractères par commentaire', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentMaxlength']) + ]); ?> +
+ +
+
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentNotification']), + ]); ?> +
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentGroupNotification']), + 'help' => 'Editeurs = éditeurs + administrateurs
Membres = membres + éditeurs + administrateurs' + ]); ?> +
+
+
+
+
+ diff --git a/module/download/view/index/index.css b/module/download/view/index/index.css new file mode 100644 index 00000000..6d358cfd --- /dev/null +++ b/module/download/view/index/index.css @@ -0,0 +1,68 @@ +.rowitem { + margin-bottom: 10px !important; +} +.downloadPicture { + float: none; + border: 1px; +} +.downloadPicture img { + width: 100%; + height: auto; + /* + border:1px solid lightgray; + box-shadow: 1px 1px 5px darkgray; + */ +} + +.downloadPicture:hover { + opacity: .7; +} +.row:after { + content: " "; + display: table; + clear: both; +} +.downloadComment { + padding-right: 10px; + float: right; +} +.downloadTitle { + /*background-color: #ECEFF1;*/ + padding: 0px; + margin-bottom: 5px; +} +.downloadContent { + float: left; + margin-top: 5px; +} +.downloadDate { + font-size:0.8em; + font-style: italic; + /* + color: grey; + */ +} +@media (max-width: 768px) { + .downloadContent { + display: none; + } + + .downloadPicture img { + width: 50% ; + display: block; + margin-left: auto; + margin-right: auto; + } +} + +/* +* Flux RSS +*/ +#rssFeed { + text-align: right; + float: right; +} +#rssFeed p { + display: inline; + vertical-align: top; +} \ No newline at end of file diff --git a/module/download/view/index/index.php b/module/download/view/index/index.php new file mode 100644 index 00000000..387ba0f1 --- /dev/null +++ b/module/download/view/index/index.php @@ -0,0 +1,67 @@ + +
+
+ $item): ?> +
+
+ makeThumb( self::FILE_DIR . 'source/' . $item['picture'], + self::FILE_DIR . 'thumb/' . $thumb, + self::THUMBS_WIDTH); + } + + ?> + + <?php echo $item['picture']; ?> + +
+
+ +
+
+ +
+
+ + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + + + + + \ No newline at end of file diff --git a/module/download/view/item/item.css b/module/download/view/item/item.css new file mode 100644 index 00000000..445f719d --- /dev/null +++ b/module/download/view/item/item.css @@ -0,0 +1,63 @@ + +#sectionTitle { + margin-top: 0; + margin-bottom: 5px; +} +.downloaditemPicture { + height: auto; + border:1px solid lightgray; + box-shadow: 1px 1px 5px; +} +.downloaditemPictureleft { + float: left; + margin: 15px 10px 5px 0 ; +} +.downloaditemPictureright { + 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 ; +} + +#downloaditemCommentShow { + cursor: text; +} +#downloaditemOr { + padding: 10px; +} +.downloadDate { + font-size:0.8em; + font-style: italic; + color: grey; +} +@media (max-width: 767px) { + .downloaditemPicture { + height:auto; + max-width: 100%;} + } + + +#rssFeed { + text-align: right; + float: right; +} +#rssFeed p { + display: inline; + vertical-align: top; +} diff --git a/module/download/view/item/item.js.php b/module/download/view/item/item.js.php new file mode 100644 index 00000000..1b1ace98 --- /dev/null +++ b/module/download/view/item/item.js.php @@ -0,0 +1,43 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +/** + * Affiche le bloc pour rédiger un commentaire + */ +var commentShowDOM = $("#downloaditemCommentShow"); +commentShowDOM.on("click focus", function() { + $("#downloaditemCommentShowWrapper").fadeOut(function() { + $("#downloaditemCommentWrapper").fadeIn(); + $("#downloaditemCommentContent").trigger("focus"); + }); +}); +if($("#downloaditemCommentWrapper").find("textarea.notice,input.notice").length) { + commentShowDOM.trigger("click"); +} + +/** + * Cache le bloc pour rédiger un commentaire + */ +$("#downloaditemCommentHide").on("click focus", function() { + $("#downloaditemCommentWrapper").fadeOut(function() { + $("#downloaditemCommentShowWrapper").fadeIn(); + $("#downloaditemCommentContent").val(""); + $("#downloaditemCommentAuthor").val(""); + }); +}); + +/** + * Force le scroll vers les commentaires en cas d'erreur + */ +$("#downloaditemCommentForm").on("submit", function() { + $(location).attr("href", "#comment"); +}); \ No newline at end of file diff --git a/module/download/view/item/item.php b/module/download/view/item/item.php new file mode 100644 index 00000000..48769b19 --- /dev/null +++ b/module/download/view/item/item.php @@ -0,0 +1,187 @@ +
+
+
+
+ + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); + $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) + ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) + : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); + echo $date . ' à ' . $heure; + ?> +
+
+
+ getUser('password') === $this->getInput('ZWII_USER_PASSWORD') + AND + ( // Propriétaire + ( + $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_OWNER + AND ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'userId']) === $this->getUser('id') + OR $this->getUser('group') === self::GROUP_ADMIN ) + ) + OR ( + // Groupe + ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === self::GROUP_ADMIN + OR $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === self::GROUP_MODERATOR) + AND $this->getUser('group') >= $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) + ) + OR ( + // Tout le monde + $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_ALL + AND $this->getUser('group') >= $module::$actions['config'] + ) + ) + ): ?> + helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], + 'value' => 'Editer' + ]); ?> + +
+
+
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']); ?> + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . + '" alt="' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . '">'; + ?> +
+
+
+
+ self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file']), + 'value' => 'Télécharger' + ]); ?> +
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileVersion']); ?> +
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate']))); + ?> + +
+
+
+
+ getData(['module', $this->getUrl(0),'items', $this->getUrl(1), 'content']); ?> +
+
+

+
+
+ + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + + + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentClose'])): ?> +

Cet item ne reçoit pas de commentaire.

+ +

+ + + + 0 ? $commentsNb . ' ' . 'commentaire' . $s : 'Pas encore de commentaire'; ?> +

+ + 'Rédiger un commentaire...', + 'readonly' => true + ]); ?> +
+ getUser('password') === $this->getInput('ZWII_USER_PASSWORD')): ?> + 'Nom', + 'readonly' => true, + 'value' => $module::$editCommentSignature + ]); ?> + $this->getUser('id') + ]); ?> + +
+
+ 'Nom' + ]); ?> +
+
+
Ou
+
+
+ helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl()) . '__comment', + 'value' => 'Connexion' + ]); ?> +
+
+ + 'Commentaire avec maximum '.$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']).' caractères', + 'class' => 'editorWysiwygComment', + 'noDirty' => true, + 'maxlength' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']) + ]); ?> +
+ getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')): ?> +
+
+ $this->getData(['config','captchaStrong']) + ]); ?> +
+
+ +
+
+ 'buttonGrey', + 'value' => 'Annuler' + ]); ?> +
+
+ 'Envoyer', + 'ico' => '' + ]); ?> +
+
+
+ +
+
+ $comment): ?> +
+

+ le + +

+ +
+
+ +
\ No newline at end of file diff --git a/module/download/view/rss/rss.php b/module/download/view/rss/rss.php new file mode 100644 index 00000000..4b8f6d3c --- /dev/null +++ b/module/download/view/rss/rss.php @@ -0,0 +1 @@ + \ No newline at end of file From 73aeea68747c75a895c42b0d400ceec19fc99b4e Mon Sep 17 00:00:00 2001 From: fredtempez Date: Fri, 12 Feb 2021 08:13:25 +0100 Subject: [PATCH 02/18] File stats in progress --- module/download/download.php | 54 +++++++++++++++++++++++++++++- module/download/view/item/item.php | 3 +- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/module/download/download.php b/module/download/download.php index 8799daac..c93a3f7f 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -28,7 +28,8 @@ class download extends common { 'delete' => self::GROUP_MODERATOR, 'edit' => self::GROUP_MODERATOR, 'index' => self::GROUP_VISITOR, - 'rss' => self::GROUP_VISITOR + 'rss' => self::GROUP_VISITOR, + 'downloadFile' => self::GROUP_VISITOR ]; public static $items = []; @@ -179,6 +180,7 @@ class download extends common { 'file' => $this->getInput('downloadAddFile', helper::FILTER_STRING_SHORT, true), 'fileVersion' => $this->getInput('downloadAddFileVersion', helper::FILTER_STRING_SHORT, true), 'fileDate' => $this->getInput('downloadAddFileDate', helper::FILTER_DATETIME, true), + 'fileCount' => 0, 'publishedOn' => $this->getInput('downloadAddPublishedOn', helper::FILTER_DATETIME, true), 'state' => $this->getInput('downloadAddState', helper::FILTER_BOOLEAN), 'title' => $this->getInput('downloadAddTitle', helper::FILTER_STRING_SHORT, true), @@ -537,6 +539,7 @@ class download extends common { 'file' => $this->getInput('downloadEditFile', helper::FILTER_STRING_SHORT, true), 'fileVersion' => $this->getInput('downloadEditFileVersion', helper::FILTER_STRING_SHORT, true), 'fileDate' => $this->getInput('downloadEditFileDate', helper::FILTER_DATETIME, true), + 'fileCount' => $this->getData(['module',$this->getUrl(0), 'items', $itemId, 'fileCount']), 'publishedOn' => $this->getInput('downloadEditPublishedOn', helper::FILTER_DATETIME, true), 'state' => $this->getInput('downloadEditState', helper::FILTER_BOOLEAN), 'title' => $this->getInput('downloadEditTitle', helper::FILTER_STRING_SHORT, true), @@ -759,5 +762,54 @@ class download extends common { return $this->getData(['user', $userId, 'firstname']); } } + + /** + * Initie un téléchargement protégé + */ + public function downloadFile() { + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0), + 'notification' => 'Action non autorisée' + ]); + } + // Téléchargement + else { + $filePath = self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); + $fileName = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); + if (file_exists($filePath)) { + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . $fileName . '"'); + header('Content-Length: ' . filesize($filePath)); + readfile( $filePath); + // Incrémenter le compteur + $this->setData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileCount', helper::filter($this->setData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileCount']) + 1, helper::FILTER_INT) ]); + // Valeurs en sortie + $this->addOutput([ + 'display' => self::DISPLAY_RAW + ]); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Configuration', + 'view' => 'index' + ]); + } else { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0), + 'notification' => 'Aucun fichier à télécharger', + 'state' => false + ]); + } + } + } } diff --git a/module/download/view/item/item.php b/module/download/view/item/item.php index 48769b19..477e4a67 100644 --- a/module/download/view/item/item.php +++ b/module/download/view/item/item.php @@ -57,7 +57,8 @@
self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file']), + //'href' => self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file']), + 'href' => helper::baseUrl() . $this->getUrl(0) . '/downloadFile/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], 'value' => 'Télécharger' ]); ?>
From 5fda899e884f09ce51c1a7ee0fa73c660da46d4a Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Fri, 12 Feb 2021 10:32:12 +0100 Subject: [PATCH 03/18] stats ok --- module/download/download.php | 24 +++++++++++++++++++----- module/download/view/config/config.php | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/module/download/download.php b/module/download/download.php index c93a3f7f..f99bbd9a 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -180,7 +180,7 @@ class download extends common { 'file' => $this->getInput('downloadAddFile', helper::FILTER_STRING_SHORT, true), 'fileVersion' => $this->getInput('downloadAddFileVersion', helper::FILTER_STRING_SHORT, true), 'fileDate' => $this->getInput('downloadAddFileDate', helper::FILTER_DATETIME, true), - 'fileCount' => 0, + 'fileStats' => [], 'publishedOn' => $this->getInput('downloadAddPublishedOn', helper::FILTER_DATETIME, true), 'state' => $this->getInput('downloadAddState', helper::FILTER_BOOLEAN), 'title' => $this->getInput('downloadAddTitle', helper::FILTER_STRING_SHORT, true), @@ -424,6 +424,9 @@ class download extends common { $toApprove = 0; $approved = count($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'comment'])); } + // Nombre de téléchargements + $stats = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'fileStats']), 'time'); + // Met en forme le tableau $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) @@ -437,6 +440,7 @@ class download extends common { '', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileVersion']), $date .' à '. $heure, + count($stats), self::$states[$this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'state'])], // Bouton pour afficher les commentaires de l'item template::button('downloadConfigComment' . $itemIds[$i], [ @@ -539,7 +543,7 @@ class download extends common { 'file' => $this->getInput('downloadEditFile', helper::FILTER_STRING_SHORT, true), 'fileVersion' => $this->getInput('downloadEditFileVersion', helper::FILTER_STRING_SHORT, true), 'fileDate' => $this->getInput('downloadEditFileDate', helper::FILTER_DATETIME, true), - 'fileCount' => $this->getData(['module',$this->getUrl(0), 'items', $itemId, 'fileCount']), + 'fileStats' => $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileStats']), 'publishedOn' => $this->getInput('downloadEditPublishedOn', helper::FILTER_DATETIME, true), 'state' => $this->getInput('downloadEditState', helper::FILTER_BOOLEAN), 'title' => $this->getInput('downloadEditTitle', helper::FILTER_STRING_SHORT, true), @@ -786,12 +790,22 @@ class download extends common { $filePath = self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); $fileName = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); if (file_exists($filePath)) { + // Statistiques de téléchargement + $statId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); + $this->setData(['module', + $this->getUrl(0), + 'items', + $this->getUrl(2), + 'fileStats', + $statId, [ + 'time' => time(), + 'ip' => helper::getIp() + ]]); + // Formatage http header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $fileName . '"'); header('Content-Length: ' . filesize($filePath)); readfile( $filePath); - // Incrémenter le compteur - $this->setData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileCount', helper::filter($this->setData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileCount']) + 1, helper::FILTER_INT) ]); // Valeurs en sortie $this->addOutput([ 'display' => self::DISPLAY_RAW @@ -805,7 +819,7 @@ class download extends common { // Valeurs en sortie $this->addOutput([ 'redirect' => helper::baseUrl() . $this->getUrl(0), - 'notification' => 'Aucun fichier à télécharger', + 'notification' => 'Le fichier n\'existe pas', 'state' => false ]); } diff --git a/module/download/view/config/config.php b/module/download/view/config/config.php index 1a3b42c3..90dfa938 100644 --- a/module/download/view/config/config.php +++ b/module/download/view/config/config.php @@ -41,7 +41,7 @@
- + From 35408d32eb87e140e50fd70d20f7ce4ba2a700fb Mon Sep 17 00:00:00 2001 From: fredtempez Date: Fri, 12 Feb 2021 14:25:51 +0100 Subject: [PATCH 04/18] Stats en cours --- module/download/download.php | 1697 +++++++------- module/download/vendor/FeedWriter/ATOM.php | 76 +- module/download/vendor/FeedWriter/Feed.php | 2034 ++++++++--------- .../FeedWriter/InvalidOperationException.php | 66 +- module/download/vendor/FeedWriter/Item.php | 826 +++---- module/download/vendor/FeedWriter/README.md | 84 +- module/download/vendor/FeedWriter/RSS1.php | 74 +- module/download/vendor/FeedWriter/RSS2.php | 74 +- module/download/view/add/add.css | 34 +- module/download/view/add/add.js.php | 106 +- module/download/view/add/add.php | 250 +- module/download/view/comment/comment.css | 34 +- module/download/view/comment/comment.js.php | 124 +- module/download/view/comment/comment.php | 44 +- module/download/view/config/config.css | 34 +- module/download/view/config/config.js.php | 40 +- module/download/view/config/config.php | 104 +- module/download/view/edit/edit.css | 34 +- module/download/view/edit/edit.js.php | 130 +- module/download/view/edit/edit.php | 290 +-- module/download/view/index/index.css | 134 +- module/download/view/index/index.php | 132 +- module/download/view/item/item.css | 126 +- module/download/view/item/item.js.php | 84 +- module/download/view/item/item.php | 374 +-- module/download/view/stats/stats.css | 18 + module/download/view/stats/stats.php | 0 27 files changed, 3540 insertions(+), 3483 deletions(-) create mode 100644 module/download/view/stats/stats.css create mode 100644 module/download/view/stats/stats.php diff --git a/module/download/download.php b/module/download/download.php index f99bbd9a..51c64ded 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -1,829 +1,868 @@ - - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - -class download extends common { - - const EDIT_OWNER = 'owner'; - const EDIT_GROUP = 'group'; - const EDIT_ALL = 'all'; - - public static $actions = [ - 'add' => self::GROUP_MODERATOR, - 'comment' => self::GROUP_MODERATOR, - 'commentApprove' => self::GROUP_MODERATOR, - 'commentDelete' => self::GROUP_MODERATOR, - 'commentDeleteAll' => self::GROUP_MODERATOR, - 'config' => self::GROUP_MODERATOR, - 'delete' => self::GROUP_MODERATOR, - 'edit' => self::GROUP_MODERATOR, - 'index' => self::GROUP_VISITOR, - 'rss' => self::GROUP_VISITOR, - 'downloadFile' => self::GROUP_VISITOR - ]; - - public static $items = []; - - // Signature de l'item - public static $itemSignature = ''; - - // 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 ', - ]; - - //Paramètre longueur maximale des commentaires en nb de caractères - public static $commentLength = [ - '500' => '500', - '1000' => '1000', - '2000' => '2000', - '5000' => '5000', - '10000' => '10000' - ]; - - // Permissions d'un item - public static $itemConsent = [ - self::EDIT_ALL => 'Tous les groupes', - self::EDIT_GROUP => 'Groupe du propriétaire', - self::EDIT_OWNER => 'Propriétaire' - ]; - - - public static $users = []; - - const VERSION = '4.2'; - const REALNAME = 'Téléchargement'; - - /** - * Flux RSS - */ - public function rss() { - // Inclure les classes - include_once 'module/news/vendor/FeedWriter/Item.php'; - include_once 'module/news/vendor/FeedWriter/Feed.php'; - include_once 'module/news/vendor/FeedWriter/RSS2.php'; - include_once 'module/news/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'])); - $feeds->setLink(helper::baseUrl() . $this->getUrl(0)); - $feeds->setDescription($this->getData (['page', $this->getUrl(0), 'metaDescription'])); - $feeds->setChannelElement('language', 'fr-FR'); - $feeds->setDate(date('r',time())); - $feeds->addGenerator(); - // Corps des items - $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC'); - $itemIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'items']), 'state', 'SORT_DESC'); - foreach($itemIdsPublishedOns as $itemId => $itemPublishedOn) { - if($itemPublishedOn <= time() AND $itemIdsStates[$itemId]) { - // Miniature - $parts = explode('/',$this->getData(['module', $this->getUrl(0), 'items', $itemId, 'picture'])); - $thumb = str_replace ($parts[(count($parts)-1)],'mini_' . $parts[(count($parts)-1)], $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'picture'])); - // Créer les items du flux - $newsitem = $feeds->createNewItem(); - // Signature de l'item - $author = $this->signature($this->getData(['module', $this->getUrl(0), 'items', $itemId, 'userId'])); - $newsitem->addElementArray([ - 'title' => $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'title']), - 'link' => helper::baseUrl() .$this->getUrl(0) . '/' . $itemId, - 'description' => '' . $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'title'])
-									 . '' . - $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'content']), - ]); - $newsitem->setAuthor($author,'no@mail.com'); - $newsitem->setId(helper::baseUrl() .$this->getUrl(0) . '/' . $itemId); - $newsitem->setDate(date('r', $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'publishedOn']))); - $imageData = getimagesize(helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb); - $newsitem->addEnclosure( helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb, - $imageData[0] * $imageData[1], - $imageData['mime'] - ); - $feeds->addItem($newsitem); - } - } - - // Valeurs en sortie - $this->addOutput([ - 'display' => self::DISPLAY_RSS, - 'content' => $feeds->generateFeed(), - 'view' => 'rss' - ]); - } - - /** - * Édition - */ - public function add() { - // Soumission du formulaire - if($this->isPost()) { - // Modification de l'userId - if($this->getUser('group') === self::GROUP_ADMIN){ - $newuserid = $this->getInput('downloadAddUserId', helper::FILTER_STRING_SHORT, true); - } - else{ - $newuserid = $this->getUser('id'); - } - // Incrémente l'id de l'item - $itemId = helper::increment($this->getInput('downloadAddTitle', helper::FILTER_ID), $this->getData(['page'])); - $itemId = helper::increment($itemId, (array) $this->getData(['module', $this->getUrl(0)])); - $itemId = helper::increment($itemId, array_keys(self::$actions)); - // Crée l'item - $this->setData(['module', - $this->getUrl(0), - 'items', - $itemId, [ - 'comment' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment']), - 'content' => $this->getInput('downloadAddContent', null), - 'picture' => $this->getInput('downloadAddPicture', helper::FILTER_STRING_SHORT, true), - 'file' => $this->getInput('downloadAddFile', helper::FILTER_STRING_SHORT, true), - 'fileVersion' => $this->getInput('downloadAddFileVersion', helper::FILTER_STRING_SHORT, true), - 'fileDate' => $this->getInput('downloadAddFileDate', helper::FILTER_DATETIME, true), - 'fileStats' => [], - 'publishedOn' => $this->getInput('downloadAddPublishedOn', helper::FILTER_DATETIME, true), - 'state' => $this->getInput('downloadAddState', helper::FILTER_BOOLEAN), - 'title' => $this->getInput('downloadAddTitle', helper::FILTER_STRING_SHORT, true), - 'userId' => $newuserid, - 'editConsent' => $this->getInput('downloadAddConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('downloadAddConsent'), - 'commentMaxlength' => $this->getInput('downloadAddCommentMaxlength'), - 'commentApproved' => $this->getInput('downloadAddCommentApproved', helper::FILTER_BOOLEAN), - 'commentClose' => $this->getInput('downloadAddCommentClose', helper::FILTER_BOOLEAN), - 'commentNotification' => $this->getInput('downloadAddCommentNotification', helper::FILTER_BOOLEAN), - 'commentGroupNotification' => $this->getInput('downloadAddCommentGroupNotification', helper::FILTER_INT) - ] - ]); - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Nouvel item créé', - 'state' => true - ]); - } - // Liste des utilisateurs - self::$users = helper::arrayCollumn($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' => 'Nouvel item', - 'vendor' => [ - 'flatpickr', - 'tinymce' - ], - 'view' => 'add' - ]); - } - - /** - * Liste des commentaires - */ - public function comment() { - $comments = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2),'comment']); - self::$commentsDelete = template::button('downloadCommentDeleteAll', [ - 'class' => 'downloadCommentDeleteAll buttonRed', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDeleteAll/' . $this->getUrl(2).'/' . $_SESSION['csrf'] , - 'ico' => 'cancel', - 'value' => 'Tout effacer' - ]); - // Ids des commentaires par ordre de création - $commentIds = array_keys(helper::arrayCollumn($comments, 'createdOn', 'SORT_DESC')); - // Pagination - $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['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), 'items', $this->getUrl(2),'commentApproved']) === true) { - $buttonApproval = template::button('downloadCommentApproved' . $commentIds[$i], [ - 'class' => $comment['approval'] === true ? 'downloadCommentRejected buttonGreen' : 'downloadCommentApproved buttonRed' , - 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentApprove/' . $this->getUrl(2) . '/' . $commentIds[$i] . '/' . $_SESSION['csrf'] , - 'value' => $comment['approval'] === true ? 'A' : 'R' - ]); - } - self::$comments[] = [ - mb_detect_encoding(strftime('%d %B %Y - %H:%M', $comment['createdOn']), 'UTF-8', true) - ? strftime('%d %B %Y - %H:%M', $comment['createdOn']) - : utf8_encode(strftime('%d %B %Y - %H:%M', $comment['createdOn'])), - $comment['content'], - $comment['userId'] ? $this->getData(['user', $comment['userId'], 'firstname']) . ' ' . $this->getData(['user', $comment['userId'], 'lastname']) : $comment['author'], - $buttonApproval, - template::button('downloadCommentDelete' . $commentIds[$i], [ - 'class' => 'downloadCommentDelete buttonRed', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDelete/' . $this->getUrl(2) . '/' . $commentIds[$i] . '/' . $_SESSION['csrf'] , - 'value' => template::ico('cancel') - ]) - ]; - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Gestion des commentaires : '. $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'title']), - 'view' => 'comment' - ]); - } - - /** - * Suppression de commentaire - */ - public function commentDelete() { - // Le commentaire n'existe pas - if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null) { - // Valeurs en sortie - $this->addOutput([ - 'access' => false - ]); - } - // Jeton incorrect - elseif ($this->getUrl(4) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Action non autorisée' - ]); - } - // Suppression - else { - $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]); - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment/'.$this->getUrl(2), - 'notification' => 'Commentaire supprimé', - 'state' => true - ]); - } - } - - /** - * Suppression de tous les commentaires de l'item $this->getUrl(2) - */ - public function commentDeleteAll() { - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Action non autorisée' - ]); - } - // Suppression - else { - $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment',[] ]); - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment', - 'notification' => 'Commentaires supprimés', - 'state' => true - ]); - } - } - - /** - * Approbation oou désapprobation de commentaire - */ - public function commentApprove() { - // Le commentaire n'existe pas - if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null) { - // Valeurs en sortie - $this->addOutput([ - 'access' => false - ]); - } - // Jeton incorrect - elseif ($this->getUrl(4) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Action non autorisée' - ]); - } - // Inversion du statut - else { - $approved = !$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'approval']) ; - $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), [ - 'author' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'author']), - 'content' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'content']), - 'createdOn' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'createdOn']), - 'userId' => $this->getData(['module', $this->getUrl(0), 'items', $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 ? 'Commentaire approuvé' : 'Commentaire rejeté', - 'state' => $approved - ]); - } - } - - /** - * Configuration - */ - public function config() { - // Soumission du formulaire - if($this->isPost()) { - $this->setData(['module', $this->getUrl(0), 'config',[ - 'feeds' => $this->getInput('downloadConfigShowFeeds',helper::FILTER_BOOLEAN), - 'feedsLabel' => $this->getInput('downloadConfigFeedslabel',helper::FILTER_STRING_SHORT) - ]]); - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Modifications enregistrées', - 'state' => true - ]); - } else { - // Ids des items par ordre de publication - $itemIds = array_keys(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC')); - // Gestion des droits d'accès - $filterData=[]; - foreach ($itemIds as $key => $value) { - if ( - ( // Propriétaire - $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) === self::EDIT_OWNER - AND ( $this->getData(['module', $this->getUrl(0), 'items', $value,'userId']) === $this->getUser('id') - OR $this->getUser('group') === self::GROUP_ADMIN ) - ) - - OR ( - // Groupe - $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) !== self::EDIT_OWNER - AND $this->getUser('group') >= $this->getData(['module',$this->getUrl(0), 'items', $value,'editConsent']) - ) - OR ( - // Tout le monde - $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) === self::EDIT_ALL - ) - ) { - $filterData[] = $value; - } - } - $itemIds = $filterData; - // Pagination - $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); - // Liste des pages - self::$pages = $pagination['pages']; - // items en fonction de la pagination - for($i = $pagination['first']; $i < $pagination['last']; $i++) { - // Nombre de commentaires à approuver et approuvés - $approvals = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$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), 'items', $itemIds[$i],'comment'])); - } - // Nombre de téléchargements - $stats = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'fileStats']), 'time'); - - // Met en forme le tableau - $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) - ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) - : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); - $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) - ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) - : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); - self::$items[] = [ - '' . - $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'title']) . - '', - $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileVersion']), - $date .' à '. $heure, - count($stats), - self::$states[$this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'state'])], - // Bouton pour afficher les commentaires de l'item - template::button('downloadConfigComment' . $itemIds[$i], [ - 'class' => ($toApprove || $approved ) > 0 ? 'buttonBlue' : 'buttonGrey' , - 'href' => ($toApprove || $approved ) > 0 ? helper::baseUrl() . $this->getUrl(0) . '/comment/' . $itemIds[$i] : '', - 'value' => $toApprove > 0 ? $toApprove . '/' . $approved : $approved - ]), - template::button('downloadConfigEdit' . $itemIds[$i], [ - 'href' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $itemIds[$i] . '/' . $_SESSION['csrf'], - 'value' => template::ico('pencil') - ]), - template::button('downloadConfigDelete' . $itemIds[$i], [ - 'class' => 'downloadConfigDelete buttonRed', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $itemIds[$i] . '/' . $_SESSION['csrf'], - 'value' => template::ico('cancel') - ]) - ]; - } - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Configuration du module', - 'view' => 'config' - ]); - } - } - - /** - * Suppression - */ - public function delete() { - if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { - // Valeurs en sortie - $this->addOutput([ - 'access' => false - ]); - } - // Jeton incorrect - elseif ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Action non autorisée' - ]); - } - // Suppression - else { - $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]); - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Item supprimé', - 'state' => true - ]); - } - } - - /** - * Édition - */ - public function edit() { - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Action non autorisée' - ]); - } - // L'item n'existe pas - if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { - // Valeurs en sortie - $this->addOutput([ - 'access' => false - ]); - } - // L'item existe - else { - // Soumission du formulaire - if($this->isPost()) { - if($this->getUser('group') === self::GROUP_ADMIN){ - $newuserid = $this->getInput('downloadEditUserId', helper::FILTER_STRING_SHORT, true); - } - else{ - $newuserid = $this->getUser('id'); - } - $itemId = $this->getInput('downloadEditTitle', helper::FILTER_ID, true); - // Incrémente le nouvel id de l'item - if($itemId !== $this->getUrl(2)) { - $itemId = helper::increment($itemId, $this->getData(['page'])); - $itemId = helper::increment($itemId, $this->getData(['module', $this->getUrl(0),'items'])); - $itemId = helper::increment($itemId, array_keys(self::$actions)); - } - $this->setData(['module', - $this->getUrl(0), - 'items', - $itemId, [ - 'comment' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment']), - 'content' => $this->getInput('downloadEditContent', null), - 'picture' => $this->getInput('downloadEditPicture', helper::FILTER_STRING_SHORT, true), - 'file' => $this->getInput('downloadEditFile', helper::FILTER_STRING_SHORT, true), - 'fileVersion' => $this->getInput('downloadEditFileVersion', helper::FILTER_STRING_SHORT, true), - 'fileDate' => $this->getInput('downloadEditFileDate', helper::FILTER_DATETIME, true), - 'fileStats' => $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileStats']), - 'publishedOn' => $this->getInput('downloadEditPublishedOn', helper::FILTER_DATETIME, true), - 'state' => $this->getInput('downloadEditState', helper::FILTER_BOOLEAN), - 'title' => $this->getInput('downloadEditTitle', helper::FILTER_STRING_SHORT, true), - 'userId' => $newuserid, - 'editConsent' => $this->getInput('downloadEditConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('downloadEditConsent'), - 'commentMaxlength' => $this->getInput('downloadEditCommentMaxlength'), - 'commentApproved' => $this->getInput('downloadEditCommentApproved', helper::FILTER_BOOLEAN), - 'commentClose' => $this->getInput('downloadEditCommentClose', helper::FILTER_BOOLEAN), - 'commentNotification' => $this->getInput('downloadEditCommentNotification', helper::FILTER_BOOLEAN), - 'commentGroupNotification' => $this->getInput('downloadEditCommentGroupNotification', helper::FILTER_INT) - ] - ]); - // Supprime l'ancien item - if($itemId !== $this->getUrl(2)) { - $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]); - } - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Modifications enregistrées', - 'state' => true - ]); - } - // Liste des utilisateurs - self::$users = helper::arrayCollumn($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_MODERATOR) { - 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), 'items', $this->getUrl(2), 'title']), - 'vendor' => [ - 'flatpickr', - 'tinymce' - ], - 'view' => 'edit' - ]); - } - } - - /** - * Accueil (deux affichages en un pour éviter une url à rallonge) - */ - public function index() { - // Affichage d'un item - 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'item n'existe pas - if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1)]) === null) { - // Valeurs en sortie - $this->addOutput([ - 'access' => false - ]); - } - // L'item existe - else { - // Soumission du formulaire - if($this->isPost()) { - // Check la captcha - if( - $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD') - //AND $this->getInput('downloaditemcaptcha', helper::FILTER_INT) !== $this->getInput('downloaditemcaptchaFirstNumber', helper::FILTER_INT) + $this->getInput('downloaditemcaptchaSecondNumber', helper::FILTER_INT)) - AND password_verify($this->getInput('downloadItemCaptcha', helper::FILTER_INT), $this->getInput('downloadItemCaptchaResult') ) === false ) - { - self::$inputNotices['downloadItemCaptcha'] = 'Incorrect'; - } - // Crée le commentaire - $commentId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment'])); - $content = $this->getInput('downloadItemContent', false); - $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentId, [ - 'author' => $this->getInput('downloadItemAuthor', helper::FILTER_STRING_SHORT, empty($this->getInput('downloadItemUserId')) ? TRUE : FALSE), - 'content' => $content, - 'createdOn' => time(), - 'userId' => $this->getInput('downloadItemUserId'), - 'approval' => !$this->getData(['module', $this->getUrl(0), 'items', $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), 'items', $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), 'items', $this->getUrl(1), 'commentApproved']) === true ? 'Commentaire déposé en attente d\'approbation': 'Commentaire déposé'; - if ($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentNotification']) === true) { - $error = 0; - foreach($to as $key => $adress){ - $sent = $this->sendMail( - $adress, - 'Nouveau commentaire déposé', - 'Bonjour' . ' ' . $firstname[$key] . ' ' . $lastname[$key] . ',

' . - 'L\'item ' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'title']) . ' a reçu un nouveau commentaire.

', - '' - ); - if( $sent === false) $error++; - } - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl() . '#comment', - 'notification' => ($error === 0 ? $notification . '
Une notification a été envoyée.' : $notification . '
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), 'items', $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'item - self::$nbCommentsApproved = count($commentsApproved); - } - $commentIds = array_keys(helper::arrayCollumn($commentsApproved, 'createdOn', 'SORT_DESC')); - // Pagination - $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['config','itemsperPage']),'#comment'); - // Liste des pages - self::$pages = $pagination['pages']; - // Signature de l'item - self::$itemSignature = $this->signature($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'userId'])); - // Signature du commentaire édité - if($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')) { - 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), 'items', $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), 'items', $this->getUrl(1), 'comment', $commentIds[$i],'author']); - } - // Données du commentaire si approuvé - if ($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentIds[$i],'approval']) === true ) { - self::$comments[$commentIds[$i]] = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentIds[$i]]); - } - } - // Valeurs en sortie - $this->addOutput([ - 'showBarEditButton' => true, - 'title' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'title']), - 'vendor' => [ - 'tinymce' - ], - 'view' => 'item' - ]); - } - - } - // Liste des items - else { - // Ids des items par ordre de publication - $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'items']), 'publishedOn', 'SORT_DESC'); - $itemIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'state', 'SORT_DESC'); - $itemIds = []; - foreach($itemIdsPublishedOns as $itemId => $itemPublishedOn) { - if($itemPublishedOn <= time() AND $itemIdsStates[$itemId]) { - $itemIds[] = $itemId; - } - } - // Pagination - $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); - // Liste des pages - self::$pages = $pagination['pages']; - // Items en fonction de la pagination - for($i = $pagination['first']; $i < $pagination['last']; $i++) { - self::$items[$itemIds[$i]] = $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i]]); - } - // Valeurs en sortie - $this->addOutput([ - 'showBarEditButton' => true, - 'showPageContent' => true, - 'view' => 'index' - ]); - } - } - - /** - * Retourne la signature d'un utilisateur - */ - private function signature($userId) { - switch ($this->getData(['user', $userId, 'signature'])){ - case 1: - return $userId; - break; - case 2: - return $this->getData(['user', $userId, 'pseudo']); - break; - case 3: - return $this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']); - break; - case 4: - return $this->getData(['user', $userId, 'lastname']) . ' ' . $this->getData(['user', $userId, 'firstname']); - break; - default: - return $this->getData(['user', $userId, 'firstname']); - } - } - - /** - * Initie un téléchargement protégé - */ - public function downloadFile() { - if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { - // Valeurs en sortie - $this->addOutput([ - 'access' => false - ]); - } - // Jeton incorrect - elseif ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0), - 'notification' => 'Action non autorisée' - ]); - } - // Téléchargement - else { - $filePath = self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); - $fileName = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); - if (file_exists($filePath)) { - // Statistiques de téléchargement - $statId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); - $this->setData(['module', - $this->getUrl(0), - 'items', - $this->getUrl(2), - 'fileStats', - $statId, [ - 'time' => time(), - 'ip' => helper::getIp() - ]]); - // Formatage http - header('Content-Type: application/octet-stream'); - header('Content-Disposition: attachment; filename="' . $fileName . '"'); - header('Content-Length: ' . filesize($filePath)); - readfile( $filePath); - // Valeurs en sortie - $this->addOutput([ - 'display' => self::DISPLAY_RAW - ]); - // Valeurs en sortie - $this->addOutput([ - 'title' => 'Configuration', - 'view' => 'index' - ]); - } else { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0), - 'notification' => 'Le fichier n\'existe pas', - 'state' => false - ]); - } - } - } -} - + + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +class download extends common { + + const EDIT_OWNER = 'owner'; + const EDIT_GROUP = 'group'; + const EDIT_ALL = 'all'; + + public static $actions = [ + 'add' => self::GROUP_MODERATOR, + 'comment' => self::GROUP_MODERATOR, + 'commentApprove' => self::GROUP_MODERATOR, + 'commentDelete' => self::GROUP_MODERATOR, + 'commentDeleteAll' => self::GROUP_MODERATOR, + 'config' => self::GROUP_MODERATOR, + 'delete' => self::GROUP_MODERATOR, + 'edit' => self::GROUP_MODERATOR, + 'stats' => self::GROUP_MODERATOR, + 'index' => self::GROUP_VISITOR, + 'rss' => self::GROUP_VISITOR, + 'downloadFile' => self::GROUP_VISITOR + ]; + + public static $items = []; + + // Signature de l'item + public static $itemSignature = ''; + + // 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 ', + ]; + + //Paramètre longueur maximale des commentaires en nb de caractères + public static $commentLength = [ + '500' => '500', + '1000' => '1000', + '2000' => '2000', + '5000' => '5000', + '10000' => '10000' + ]; + + // Permissions d'un item + public static $itemConsent = [ + self::EDIT_ALL => 'Tous les groupes', + self::EDIT_GROUP => 'Groupe du propriétaire', + self::EDIT_OWNER => 'Propriétaire' + ]; + + + public static $users = []; + + const VERSION = '4.2'; + const REALNAME = 'Téléchargement'; + + /** + * Flux RSS + */ + public function rss() { + // Inclure les classes + include_once 'module/news/vendor/FeedWriter/Item.php'; + include_once 'module/news/vendor/FeedWriter/Feed.php'; + include_once 'module/news/vendor/FeedWriter/RSS2.php'; + include_once 'module/news/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'])); + $feeds->setLink(helper::baseUrl() . $this->getUrl(0)); + $feeds->setDescription($this->getData (['page', $this->getUrl(0), 'metaDescription'])); + $feeds->setChannelElement('language', 'fr-FR'); + $feeds->setDate(date('r',time())); + $feeds->addGenerator(); + // Corps des items + $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC'); + $itemIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'items']), 'state', 'SORT_DESC'); + foreach($itemIdsPublishedOns as $itemId => $itemPublishedOn) { + if($itemPublishedOn <= time() AND $itemIdsStates[$itemId]) { + // Miniature + $parts = explode('/',$this->getData(['module', $this->getUrl(0), 'items', $itemId, 'picture'])); + $thumb = str_replace ($parts[(count($parts)-1)],'mini_' . $parts[(count($parts)-1)], $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'picture'])); + // Créer les items du flux + $newsitem = $feeds->createNewItem(); + // Signature de l'item + $author = $this->signature($this->getData(['module', $this->getUrl(0), 'items', $itemId, 'userId'])); + $newsitem->addElementArray([ + 'title' => $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'title']), + 'link' => helper::baseUrl() .$this->getUrl(0) . '/' . $itemId, + 'description' => '' . $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'title'])
+									 . '' . + $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'content']), + ]); + $newsitem->setAuthor($author,'no@mail.com'); + $newsitem->setId(helper::baseUrl() .$this->getUrl(0) . '/' . $itemId); + $newsitem->setDate(date('r', $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'publishedOn']))); + $imageData = getimagesize(helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb); + $newsitem->addEnclosure( helper::baseUrl(false) . self::FILE_DIR . 'thumb/' . $thumb, + $imageData[0] * $imageData[1], + $imageData['mime'] + ); + $feeds->addItem($newsitem); + } + } + + // Valeurs en sortie + $this->addOutput([ + 'display' => self::DISPLAY_RSS, + 'content' => $feeds->generateFeed(), + 'view' => 'rss' + ]); + } + + /** + * Édition + */ + public function add() { + // Soumission du formulaire + if($this->isPost()) { + // Modification de l'userId + if($this->getUser('group') === self::GROUP_ADMIN){ + $newuserid = $this->getInput('downloadAddUserId', helper::FILTER_STRING_SHORT, true); + } + else{ + $newuserid = $this->getUser('id'); + } + // Incrémente l'id de l'item + $itemId = helper::increment($this->getInput('downloadAddTitle', helper::FILTER_ID), $this->getData(['page'])); + $itemId = helper::increment($itemId, (array) $this->getData(['module', $this->getUrl(0)])); + $itemId = helper::increment($itemId, array_keys(self::$actions)); + // Crée l'item + $this->setData(['module', + $this->getUrl(0), + 'items', + $itemId, [ + 'comment' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment']), + 'content' => $this->getInput('downloadAddContent', null), + 'picture' => $this->getInput('downloadAddPicture', helper::FILTER_STRING_SHORT, true), + 'file' => $this->getInput('downloadAddFile', helper::FILTER_STRING_SHORT, true), + 'fileVersion' => $this->getInput('downloadAddFileVersion', helper::FILTER_STRING_SHORT, true), + 'fileDate' => $this->getInput('downloadAddFileDate', helper::FILTER_DATETIME, true), + 'fileStats' => [], + 'publishedOn' => $this->getInput('downloadAddPublishedOn', helper::FILTER_DATETIME, true), + 'state' => $this->getInput('downloadAddState', helper::FILTER_BOOLEAN), + 'title' => $this->getInput('downloadAddTitle', helper::FILTER_STRING_SHORT, true), + 'userId' => $newuserid, + 'editConsent' => $this->getInput('downloadAddConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('downloadAddConsent'), + 'commentMaxlength' => $this->getInput('downloadAddCommentMaxlength'), + 'commentApproved' => $this->getInput('downloadAddCommentApproved', helper::FILTER_BOOLEAN), + 'commentClose' => $this->getInput('downloadAddCommentClose', helper::FILTER_BOOLEAN), + 'commentNotification' => $this->getInput('downloadAddCommentNotification', helper::FILTER_BOOLEAN), + 'commentGroupNotification' => $this->getInput('downloadAddCommentGroupNotification', helper::FILTER_INT) + ] + ]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Nouvel item créé', + 'state' => true + ]); + } + // Liste des utilisateurs + self::$users = helper::arrayCollumn($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' => 'Nouvel item', + 'vendor' => [ + 'flatpickr', + 'tinymce' + ], + 'view' => 'add' + ]); + } + + /** + * Liste des commentaires + */ + public function comment() { + $comments = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2),'comment']); + self::$commentsDelete = template::button('downloadCommentDeleteAll', [ + 'class' => 'downloadCommentDeleteAll buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDeleteAll/' . $this->getUrl(2).'/' . $_SESSION['csrf'] , + 'ico' => 'cancel', + 'value' => 'Tout effacer' + ]); + // Ids des commentaires par ordre de création + $commentIds = array_keys(helper::arrayCollumn($comments, 'createdOn', 'SORT_DESC')); + // Pagination + $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['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), 'items', $this->getUrl(2),'commentApproved']) === true) { + $buttonApproval = template::button('downloadCommentApproved' . $commentIds[$i], [ + 'class' => $comment['approval'] === true ? 'downloadCommentRejected buttonGreen' : 'downloadCommentApproved buttonRed' , + 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentApprove/' . $this->getUrl(2) . '/' . $commentIds[$i] . '/' . $_SESSION['csrf'] , + 'value' => $comment['approval'] === true ? 'A' : 'R' + ]); + } + self::$comments[] = [ + mb_detect_encoding(strftime('%d %B %Y - %H:%M', $comment['createdOn']), 'UTF-8', true) + ? strftime('%d %B %Y - %H:%M', $comment['createdOn']) + : utf8_encode(strftime('%d %B %Y - %H:%M', $comment['createdOn'])), + $comment['content'], + $comment['userId'] ? $this->getData(['user', $comment['userId'], 'firstname']) . ' ' . $this->getData(['user', $comment['userId'], 'lastname']) : $comment['author'], + $buttonApproval, + template::button('downloadCommentDelete' . $commentIds[$i], [ + 'class' => 'downloadCommentDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/commentDelete/' . $this->getUrl(2) . '/' . $commentIds[$i] . '/' . $_SESSION['csrf'] , + 'value' => template::ico('cancel') + ]) + ]; + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Gestion des commentaires : '. $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'title']), + 'view' => 'comment' + ]); + } + + /** + * Suppression de commentaire + */ + public function commentDelete() { + // Le commentaire n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(4) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Suppression + else { + $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment/'.$this->getUrl(2), + 'notification' => 'Commentaire supprimé', + 'state' => true + ]); + } + } + + /** + * Suppression de tous les commentaires de l'item $this->getUrl(2) + */ + public function commentDeleteAll() { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Suppression + else { + $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment',[] ]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/comment', + 'notification' => 'Commentaires supprimés', + 'state' => true + ]); + } + } + + /** + * Approbation oou désapprobation de commentaire + */ + public function commentApprove() { + // Le commentaire n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(4) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Inversion du statut + else { + $approved = !$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'approval']) ; + $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), [ + 'author' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'author']), + 'content' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'content']), + 'createdOn' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment', $this->getUrl(3), 'createdOn']), + 'userId' => $this->getData(['module', $this->getUrl(0), 'items', $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 ? 'Commentaire approuvé' : 'Commentaire rejeté', + 'state' => $approved + ]); + } + } + + /** + * Configuration + */ + public function config() { + // Soumission du formulaire + if($this->isPost()) { + $this->setData(['module', $this->getUrl(0), 'config',[ + 'feeds' => $this->getInput('downloadConfigShowFeeds',helper::FILTER_BOOLEAN), + 'feedsLabel' => $this->getInput('downloadConfigFeedslabel',helper::FILTER_STRING_SHORT) + ]]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } else { + // Ids des items par ordre de publication + $itemIds = array_keys(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC')); + // Gestion des droits d'accès + $filterData=[]; + foreach ($itemIds as $key => $value) { + if ( + ( // Propriétaire + $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) === self::EDIT_OWNER + AND ( $this->getData(['module', $this->getUrl(0), 'items', $value,'userId']) === $this->getUser('id') + OR $this->getUser('group') === self::GROUP_ADMIN ) + ) + + OR ( + // Groupe + $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) !== self::EDIT_OWNER + AND $this->getUser('group') >= $this->getData(['module',$this->getUrl(0), 'items', $value,'editConsent']) + ) + OR ( + // Tout le monde + $this->getData(['module', $this->getUrl(0), 'items', $value,'editConsent']) === self::EDIT_ALL + ) + ) { + $filterData[] = $value; + } + } + $itemIds = $filterData; + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pages = $pagination['pages']; + // items en fonction de la pagination + for($i = $pagination['first']; $i < $pagination['last']; $i++) { + // Nombre de commentaires à approuver et approuvés + $approvals = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$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), 'items', $itemIds[$i],'comment'])); + } + // Nombre de téléchargements + $stats = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'fileStats']), 'time'); + // Met en forme le tableau + $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); + $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) + ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) + : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); + self::$items[] = [ + '' . + $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'title']) . + '', + $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileVersion']), + $date .' à '. $heure, + '' . count($stats) . '', + self::$states[$this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'state'])], + // Bouton pour afficher les commentaires de l'item + template::button('downloadConfigComment' . $itemIds[$i], [ + 'class' => ($toApprove || $approved ) > 0 ? 'buttonBlue' : 'buttonGrey' , + 'href' => ($toApprove || $approved ) > 0 ? helper::baseUrl() . $this->getUrl(0) . '/comment/' . $itemIds[$i] : '', + 'value' => $toApprove > 0 ? $toApprove . '/' . $approved : $approved + ]), + template::button('downloadConfigEdit' . $itemIds[$i], [ + 'href' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $itemIds[$i] . '/' . $_SESSION['csrf'], + 'value' => template::ico('pencil') + ]), + template::button('downloadConfigDelete' . $itemIds[$i], [ + 'class' => 'downloadConfigDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $itemIds[$i] . '/' . $_SESSION['csrf'], + 'value' => template::ico('cancel') + ]) + ]; + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Configuration du module', + 'view' => 'config' + ]); + } + } + + /** + * Suppression + */ + public function delete() { + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // Suppression + else { + $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Item supprimé', + 'state' => true + ]); + } + } + + /** + * Édition + */ + public function edit() { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // L'item n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // L'item existe + else { + // Soumission du formulaire + if($this->isPost()) { + if($this->getUser('group') === self::GROUP_ADMIN){ + $newuserid = $this->getInput('downloadEditUserId', helper::FILTER_STRING_SHORT, true); + } + else{ + $newuserid = $this->getUser('id'); + } + $itemId = $this->getInput('downloadEditTitle', helper::FILTER_ID, true); + // Incrémente le nouvel id de l'item + if($itemId !== $this->getUrl(2)) { + $itemId = helper::increment($itemId, $this->getData(['page'])); + $itemId = helper::increment($itemId, $this->getData(['module', $this->getUrl(0),'items'])); + $itemId = helper::increment($itemId, array_keys(self::$actions)); + } + $this->setData(['module', + $this->getUrl(0), + 'items', + $itemId, [ + 'comment' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'comment']), + 'content' => $this->getInput('downloadEditContent', null), + 'picture' => $this->getInput('downloadEditPicture', helper::FILTER_STRING_SHORT, true), + 'file' => $this->getInput('downloadEditFile', helper::FILTER_STRING_SHORT, true), + 'fileVersion' => $this->getInput('downloadEditFileVersion', helper::FILTER_STRING_SHORT, true), + 'fileDate' => $this->getInput('downloadEditFileDate', helper::FILTER_DATETIME, true), + 'fileStats' => $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileStats']), + 'publishedOn' => $this->getInput('downloadEditPublishedOn', helper::FILTER_DATETIME, true), + 'state' => $this->getInput('downloadEditState', helper::FILTER_BOOLEAN), + 'title' => $this->getInput('downloadEditTitle', helper::FILTER_STRING_SHORT, true), + 'userId' => $newuserid, + 'editConsent' => $this->getInput('downloadEditConsent') === self::EDIT_GROUP ? $this->getUser('group') : $this->getInput('downloadEditConsent'), + 'commentMaxlength' => $this->getInput('downloadEditCommentMaxlength'), + 'commentApproved' => $this->getInput('downloadEditCommentApproved', helper::FILTER_BOOLEAN), + 'commentClose' => $this->getInput('downloadEditCommentClose', helper::FILTER_BOOLEAN), + 'commentNotification' => $this->getInput('downloadEditCommentNotification', helper::FILTER_BOOLEAN), + 'commentGroupNotification' => $this->getInput('downloadEditCommentGroupNotification', helper::FILTER_INT) + ] + ]); + // Supprime l'ancien item + if($itemId !== $this->getUrl(2)) { + $this->deleteData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]); + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } + // Liste des utilisateurs + self::$users = helper::arrayCollumn($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_MODERATOR) { + 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), 'items', $this->getUrl(2), 'title']), + 'vendor' => [ + 'flatpickr', + 'tinymce' + ], + 'view' => 'edit' + ]); + } + } + + /** + * Accueil (deux affichages en un pour éviter une url à rallonge) + */ + public function index() { + // Affichage d'un item + 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'item n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // L'item existe + else { + // Soumission du formulaire + if($this->isPost()) { + // Check la captcha + if( + $this->getUser('password') !== $this->getInput('ZWII_USER_PASSWORD') + //AND $this->getInput('downloaditemcaptcha', helper::FILTER_INT) !== $this->getInput('downloaditemcaptchaFirstNumber', helper::FILTER_INT) + $this->getInput('downloaditemcaptchaSecondNumber', helper::FILTER_INT)) + AND password_verify($this->getInput('downloadItemCaptcha', helper::FILTER_INT), $this->getInput('downloadItemCaptchaResult') ) === false ) + { + self::$inputNotices['downloadItemCaptcha'] = 'Incorrect'; + } + // Crée le commentaire + $commentId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment'])); + $content = $this->getInput('downloadItemContent', false); + $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentId, [ + 'author' => $this->getInput('downloadItemAuthor', helper::FILTER_STRING_SHORT, empty($this->getInput('downloadItemUserId')) ? TRUE : FALSE), + 'content' => $content, + 'createdOn' => time(), + 'userId' => $this->getInput('downloadItemUserId'), + 'approval' => !$this->getData(['module', $this->getUrl(0), 'items', $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), 'items', $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), 'items', $this->getUrl(1), 'commentApproved']) === true ? 'Commentaire déposé en attente d\'approbation': 'Commentaire déposé'; + if ($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentNotification']) === true) { + $error = 0; + foreach($to as $key => $adress){ + $sent = $this->sendMail( + $adress, + 'Nouveau commentaire déposé', + 'Bonjour' . ' ' . $firstname[$key] . ' ' . $lastname[$key] . ',

' . + 'L\'item ' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'title']) . ' a reçu un nouveau commentaire.

', + '' + ); + if( $sent === false) $error++; + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl() . '#comment', + 'notification' => ($error === 0 ? $notification . '
Une notification a été envoyée.' : $notification . '
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), 'items', $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'item + self::$nbCommentsApproved = count($commentsApproved); + } + $commentIds = array_keys(helper::arrayCollumn($commentsApproved, 'createdOn', 'SORT_DESC')); + // Pagination + $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['config','itemsperPage']),'#comment'); + // Liste des pages + self::$pages = $pagination['pages']; + // Signature de l'item + self::$itemSignature = $this->signature($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'userId'])); + // Signature du commentaire édité + if($this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD')) { + 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), 'items', $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), 'items', $this->getUrl(1), 'comment', $commentIds[$i],'author']); + } + // Données du commentaire si approuvé + if ($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentIds[$i],'approval']) === true ) { + self::$comments[$commentIds[$i]] = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'comment', $commentIds[$i]]); + } + } + // Valeurs en sortie + $this->addOutput([ + 'showBarEditButton' => true, + 'title' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'title']), + 'vendor' => [ + 'tinymce' + ], + 'view' => 'item' + ]); + } + + } + // Liste des items + else { + // Ids des items par ordre de publication + $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'items']), 'publishedOn', 'SORT_DESC'); + $itemIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'state', 'SORT_DESC'); + $itemIds = []; + foreach($itemIdsPublishedOns as $itemId => $itemPublishedOn) { + if($itemPublishedOn <= time() AND $itemIdsStates[$itemId]) { + $itemIds[] = $itemId; + } + } + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pages = $pagination['pages']; + // Items en fonction de la pagination + for($i = $pagination['first']; $i < $pagination['last']; $i++) { + self::$items[$itemIds[$i]] = $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i]]); + } + // Valeurs en sortie + $this->addOutput([ + 'showBarEditButton' => true, + 'showPageContent' => true, + 'view' => 'index' + ]); + } + } + + /** + * Retourne la signature d'un utilisateur + */ + private function signature($userId) { + switch ($this->getData(['user', $userId, 'signature'])){ + case 1: + return $userId; + break; + case 2: + return $this->getData(['user', $userId, 'pseudo']); + break; + case 3: + return $this->getData(['user', $userId, 'firstname']) . ' ' . $this->getData(['user', $userId, 'lastname']); + break; + case 4: + return $this->getData(['user', $userId, 'lastname']) . ' ' . $this->getData(['user', $userId, 'firstname']); + break; + default: + return $this->getData(['user', $userId, 'firstname']); + } + } + + /** + * Initie un téléchargement protégé + */ + public function downloadFile() { + + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0), + 'notification' => 'Action non autorisée' + ]); + } + // Téléchargement + else { + $filePath = self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); + $fileName = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); + if (file_exists($filePath)) { + // Statistiques de téléchargement + $statId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); + $this->setData(['module', + $this->getUrl(0), + 'items', + $this->getUrl(2), + 'fileStats', + $statId, [ + 'time' => time(), + 'ip' => helper::getIp() + ]]); + // Formatage http + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . $fileName . '"'); + header('Content-Length: ' . filesize($filePath)); + readfile( $filePath); + // Valeurs en sortie + $this->addOutput([ + 'display' => self::DISPLAY_RAW + ]); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Configuration', + 'view' => 'index' + ]); + } else { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0), + 'notification' => 'Le fichier n\'existe pas', + 'state' => false + ]); + } + } + } + + /** + * Ecran de consultation des données statistiques + */ + + public function stats() { + // Soumission du formulaire + if($this->isPost()) { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // L'item n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + } else { + // Construction de la page des statistiques + // Ids des items par ordre de publication + + $itemIds = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats']); + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pages = $pagination['pages']; + + for($i = $pagination['first']; $i < $pagination['last']; $i++) { + + } + + } + } +} \ No newline at end of file diff --git a/module/download/vendor/FeedWriter/ATOM.php b/module/download/vendor/FeedWriter/ATOM.php index 28494501..fa33776e 100644 --- a/module/download/vendor/FeedWriter/ATOM.php +++ b/module/download/vendor/FeedWriter/ATOM.php @@ -1,38 +1,38 @@ - - * - * 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 . - */ - -/** - * Wrapper for creating ATOM feeds - * - * @package UniversalFeedWriter - */ -class ATOM extends Feed -{ - /** - * {@inheritdoc} - */ - public function __construct() - { - parent::__construct(Feed::ATOM); - } - -} + + * + * 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 . + */ + +/** + * Wrapper for creating ATOM feeds + * + * @package UniversalFeedWriter + */ +class ATOM extends Feed +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct(Feed::ATOM); + } + +} diff --git a/module/download/vendor/FeedWriter/Feed.php b/module/download/vendor/FeedWriter/Feed.php index d33a2059..506094f1 100644 --- a/module/download/vendor/FeedWriter/Feed.php +++ b/module/download/vendor/FeedWriter/Feed.php @@ -1,1017 +1,1017 @@ - - * Copyright (C) 2010-2016 Michael Bemmerl - * - * 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 . - */ - -/** - * Universal Feed Writer class - * - * Generate RSS 1.0, RSS2.0 and ATOM Feeds - * - * @package UniversalFeedWriter - * @author Anis uddin Ahmad - * @link http://www.ajaxray.com/projects/rss - */ -abstract class Feed -{ - // RSS 0.90 Officially obsoleted by 1.0 - // RSS 0.91, 0.92, 0.93 and 0.94 Officially obsoleted by 2.0 - // So, define constants for RSS 1.0, RSS 2.0 and ATOM - - const RSS1 = 'RSS 1.0'; - const RSS2 = 'RSS 2.0'; - const ATOM = 'ATOM'; - - const VERSION = '1.1.0'; - - /** - * Collection of all channel elements - */ - private $channels = array(); - - /** - * Collection of items as object of \FeedWriter\Item class. - */ - private $items = array(); - - /** - * Collection of other version wise data. - * - * Currently used to store the 'rdf:about' attribute and image element of the channel (both RSS1 only). - */ - private $data = array(); - - /** - * The tag names which have to encoded as CDATA - */ - private $CDATAEncoding = array(); - - /** - * Collection of XML namespaces - */ - private $namespaces = array(); - - /** - * Contains the format of this feed. - */ - private $version = null; - - /** - * Constructor - * - * If no version is given, a feed in RSS 2.0 format will be generated. - * - * @param string $version the version constant (RSS1/RSS2/ATOM). - */ - protected function __construct($version = Feed::RSS2) - { - $this->version = $version; - - // Setting default encoding - $this->encoding = 'utf-8'; - - // Setting default value for essential channel element - $this->setTitle($version . ' Feed'); - - // Add some default XML namespaces - $this->namespaces['content'] = 'http://purl.org/rss/1.0/modules/content/'; - $this->namespaces['wfw'] = 'http://wellformedweb.org/CommentAPI/'; - $this->namespaces['atom'] = 'http://www.w3.org/2005/Atom'; - $this->namespaces['rdf'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; - $this->namespaces['rss1'] = 'http://purl.org/rss/1.0/'; - $this->namespaces['dc'] = 'http://purl.org/dc/elements/1.1/'; - $this->namespaces['sy'] = 'http://purl.org/rss/1.0/modules/syndication/'; - - // Tag names to encode in CDATA - $this->addCDATAEncoding(array('description', 'content:encoded', 'summary')); - } - - // Start # public functions --------------------------------------------- - - /** - * Set the URLs for feed pagination. - * - * See RFC 5005, chapter 3. At least one page URL must be specified. - * - * @param string $nextURL The URL to the next page of this feed. Optional. - * @param string $previousURL The URL to the previous page of this feed. Optional. - * @param string $firstURL The URL to the first page of this feed. Optional. - * @param string $lastURL The URL to the last page of this feed. Optional. - * @link http://tools.ietf.org/html/rfc5005#section-3 - * @return self - * @throws \LogicException if none of the parameters are set. - */ - public function setPagination($nextURL = null, $previousURL = null, $firstURL = null, $lastURL = null) - { - if (empty($nextURL) && empty($previousURL) && empty($firstURL) && empty($lastURL)) - throw new \LogicException('At least one URL must be specified for pagination to work.'); - - if (!empty($nextURL)) - $this->setAtomLink($nextURL, 'next'); - - if (!empty($previousURL)) - $this->setAtomLink($previousURL, 'previous'); - - if (!empty($firstURL)) - $this->setAtomLink($firstURL, 'first'); - - if (!empty($lastURL)) - $this->setAtomLink($lastURL, 'last'); - - return $this; - } - - /** - * Add a channel element indicating the program used to generate the feed. - * - * @return self - * @throws InvalidOperationException if this method is called on an RSS1 feed. - */ - public function addGenerator() - { - if ($this->version == Feed::ATOM) - $this->setChannelElement('atom:generator', 'FeedWriter', array('uri' => 'https://github.com/mibe/FeedWriter')); - else if ($this->version == Feed::RSS2) - $this->setChannelElement('generator', 'FeedWriter'); - else - throw new InvalidOperationException('The generator element is not supported in RSS1 feeds.'); - - return $this; - } - - /** - * Add a XML namespace to the internal list of namespaces. After that, - * custom channel elements can be used properly to generate a valid feed. - * - * @access public - * @param string $prefix namespace prefix - * @param string $uri namespace name (URI) - * @return self - * @link http://www.w3.org/TR/REC-xml-names/ - * @throws \InvalidArgumentException if the prefix or uri is empty or NULL. - */ - public function addNamespace($prefix, $uri) - { - if (empty($prefix)) - throw new \InvalidArgumentException('The prefix may not be emtpy or NULL.'); - if (empty($uri)) - throw new \InvalidArgumentException('The uri may not be empty or NULL.'); - - $this->namespaces[$prefix] = $uri; - - return $this; - } - - /** - * Add a channel element to the feed. - * - * @access public - * @param string $elementName name of the channel tag - * @param string $content content of the channel tag - * @param array array of element attributes with attribute name as array key - * @param bool TRUE if this element can appear multiple times - * @return self - * @throws \InvalidArgumentException if the element name is not a string, empty or NULL. - */ - public function setChannelElement($elementName, $content, array $attributes = null, $multiple = 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.'); - - $entity['content'] = $content; - $entity['attributes'] = $attributes; - - if ($multiple === TRUE) - $this->channels[$elementName][] = $entity; - else - $this->channels[$elementName] = $entity; - - return $this; - } - - /** - * Set multiple channel elements from an array. Array elements - * should be 'channelName' => 'channelContent' format. - * - * @access public - * @param array array of channels - * @return self - */ - public function setChannelElementsFromArray(array $elementArray) - { - foreach ($elementArray as $elementName => $content) { - $this->setChannelElement($elementName, $content); - } - - return $this; - } - - /** - * Get the appropriate MIME type string for the current feed. - * - * @access public - * @return string The MIME type string. - */ - public function getMIMEType() - { - switch ($this->version) { - case Feed::RSS2 : $mimeType = "application/rss+xml"; - break; - case Feed::RSS1 : $mimeType = "application/rdf+xml"; - break; - case Feed::ATOM : $mimeType = "application/atom+xml"; - break; - default : $mimeType = "text/xml"; - } - - return $mimeType; - } - - /** - * Print the actual RSS/ATOM file - * - * Sets a Content-Type header and echoes the contents of the feed. - * Should only be used in situations where direct output is desired; - * if you need to pass a string around, use generateFeed() instead. - * - * @access public - * @param bool FALSE if the specific feed media type should be sent. - * @return void - * @throws \InvalidArgumentException if the useGenericContentType parameter is not boolean. - */ - public function printFeed($useGenericContentType = false) - { - if (!is_bool($useGenericContentType)) - throw new \InvalidArgumentException('The useGenericContentType parameter must be boolean.'); - - $contentType = "text/xml"; - - if (!$useGenericContentType) { - $contentType = $this->getMIMEType(); - } - - // Generate the feed before setting the header, so Exceptions will be nicely visible. - $feed = $this->generateFeed(); - header("Content-Type: " . $contentType . "; charset=" . $this->encoding); - echo $feed; - } - - /** - * Generate the feed. - * - * @access public - * @return string The complete feed XML. - * @throws InvalidOperationException if the link element of the feed is not set. - */ - public function generateFeed() - { - if ($this->version != Feed::ATOM && !array_key_exists('link', $this->channels)) - throw new InvalidOperationException('RSS1 & RSS2 feeds need a link element. Call the setLink method before this method.'); - - return $this->makeHeader() - . $this->makeChannels() - . $this->makeItems() - . $this->makeFooter(); - } - - /** - * Create a new Item. - * - * @access public - * @return Item instance of Item class - */ - public function createNewItem() - { - $Item = new Item($this->version); - - return $Item; - } - - /** - * Add one or more tags to the list of CDATA encoded tags - * - * @access public - * @param array $tags An array of tag names that are merged into the list of tags which should be encoded as CDATA - * @return self - */ - public function addCDATAEncoding(array $tags) - { - $this->CDATAEncoding = array_merge($this->CDATAEncoding, $tags); - - return $this; - } - - /** - * Get list of CDATA encoded properties - * - * @access public - * @return array Return an array of CDATA properties that are to be encoded as CDATA - */ - public function getCDATAEncoding() - { - return $this->CDATAEncoding; - } - - /** - * Remove tags from the list of CDATA encoded tags - * - * @access public - * @param array $tags An array of tag names that should be removed. - * @return void - */ - public function removeCDATAEncoding(array $tags) - { - // Call array_values to re-index the array. - $this->CDATAEncoding = array_values(array_diff($this->CDATAEncoding, $tags)); - } - - /** - * Add a FeedItem to the main class - * - * @access public - * @param Item $feedItem instance of Item class - * @return self - * @throws \InvalidArgumentException if the given item version mismatches. - */ - public function addItem(Item $feedItem) - { - if ($feedItem->getVersion() != $this->version) - { - $msg = sprintf('Feed type mismatch: This instance can handle %s feeds only, but item for %s feeds given.', $this->version, $feedItem->getVersion()); - throw new \InvalidArgumentException($msg); - } - - $this->items[] = $feedItem; - - return $this; - } - - // Wrapper functions ------------------------------------------------------------------- - - /** - * Set the 'encoding' attribute in the XML prolog. - * - * @access public - * @param string $encoding value of 'encoding' attribute - * @return self - * @throws \InvalidArgumentException if the encoding is not a string, empty or NULL. - */ - public function setEncoding($encoding) - { - if (empty($encoding)) - throw new \InvalidArgumentException('The encoding may not be empty or NULL.'); - if (!is_string($encoding)) - throw new \InvalidArgumentException('The encoding must be a string.'); - - $this->encoding = $encoding; - - return $this; - } - - /** - * Set the 'title' channel element - * - * @access public - * @param string $title value of 'title' channel tag - * @return self - */ - public function setTitle($title) - { - return $this->setChannelElement('title', $title); - } - - /** - * Set the date when the feed was lastly updated. - * - * This adds the 'updated' element to the feed. The value of the date parameter - * can be either an instance of the DateTime class, an integer containing a UNIX - * timestamp or a string which is parseable by PHP's 'strtotime' function. - * - * Not supported in RSS1 feeds. - * - * @access public - * @param DateTime|int|string Date which should be used. - * @return self - * @throws \InvalidArgumentException if the given date is not an instance of DateTime, a UNIX timestamp or a date string. - * @throws InvalidOperationException if this method is called on an RSS1 feed. - */ - public function setDate($date) - { - if ($this->version == Feed::RSS1) - throw new InvalidOperationException('The publication date is not supported in RSS1 feeds.'); - - // The feeds have different date formats. - $format = $this->version == Feed::ATOM ? \DATE_ATOM : \DATE_RSS; - - if ($date instanceof DateTime) - $date = $date->format($format); - else if(is_numeric($date) && $date >= 0) - $date = date($format, $date); - else if (is_string($date)) - { - $timestamp = strtotime($date); - if ($timestamp === FALSE) - throw new \InvalidArgumentException('The given date was not parseable.'); - - $date = date($format, $timestamp); - } - else - throw new \InvalidArgumentException('The given date is not an instance of DateTime, a UNIX timestamp or a date string.'); - - if ($this->version == Feed::ATOM) - $this->setChannelElement('updated', $date); - else - $this->setChannelElement('lastBuildDate', $date); - - return $this; - } - - /** - * Set a phrase or sentence describing the feed. - * - * @access public - * @param string $description Description of the feed. - * @return self - */ - public function setDescription($description) - { - if ($this->version != Feed::ATOM) - $this->setChannelElement('description', $description); - else - $this->setChannelElement('subtitle', $description); - - return $this; - } - - /** - * Set the 'link' channel element - * - * @access public - * @param string $link value of 'link' channel tag - * @return self - */ - public function setLink($link) - { - if ($this->version == Feed::ATOM) - $this->setAtomLink($link); - else - $this->setChannelElement('link', $link); - - return $this; - } - - /** - * Set custom 'link' channel elements. - * - * In ATOM feeds, only one link with alternate relation and the same combination of - * type and hreflang values. - * - * @access public - * @param string $href URI of this link - * @param string $rel relation type of the resource - * @param string $type MIME type of the target resource - * @param string $hreflang language of the resource - * @param string $title human-readable information about the resource - * @param int $length length of the resource in bytes - * @link https://www.iana.org/assignments/link-relations/link-relations.xml - * @link https://tools.ietf.org/html/rfc4287#section-4.2.7 - * @return self - * @throws \InvalidArgumentException on multiple occasions. - * @throws InvalidOperationException if the same link with the same attributes was already added to the feed. - */ - public function setAtomLink($href, $rel = null, $type = null, $hreflang = null, $title = null, $length = null) - { - $data = array('href' => $href); - - if ($rel != null) { - if (!is_string($rel) || empty($rel)) - throw new \InvalidArgumentException('rel parameter must be a string and a valid relation identifier.'); - - $data['rel'] = $rel; - } - if ($type != null) { - // 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.'); - - $data['type'] = $type; - } - if ($hreflang != null) { - // Regex used from RFC 4287, page 41 - if (!is_string($hreflang) || preg_match('/[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*/', $hreflang) != 1) - throw new \InvalidArgumentException('hreflang parameter must be a string and a valid language code.'); - - $data['hreflang'] = $hreflang; - } - if ($title != null) { - if (!is_string($title) || empty($title)) - throw new \InvalidArgumentException('title parameter must be a string and not empty.'); - - $data['title'] = $title; - } - if ($length != null) { - if (!is_int($length) || $length < 0) - throw new \InvalidArgumentException('length parameter must be a positive integer.'); - - $data['length'] = (string) $length; - } - - // ATOM spec. has some restrictions on atom:link usage - // See RFC 4287, page 12 (4.1.1) - if ($this->version == Feed::ATOM) { - foreach ($this->channels as $key => $value) { - if ($key != 'atom:link') - continue; - - // $value is an array , so check every element - foreach ($value as $linkItem) { - $attrib = $linkItem['attributes']; - // Only one link with relation alternate and same hreflang & type is allowed. - if (@$attrib['rel'] == 'alternate' && @$attrib['hreflang'] == $hreflang && @$attrib['type'] == $type) - throw new InvalidOperationException('The feed must not contain more than one link element with a' - . ' relation of "alternate" that has the same combination of type and hreflang attribute values.'); - } - } - } - - return $this->setChannelElement('atom:link', '', $data, true); - } - - /** - * Set an 'atom:link' channel element with relation=self attribute. - * Needs the full URL to this feed. - * - * @link http://www.rssboard.org/rss-profile#namespace-elements-atom-link - * @access public - * @param string $url URL to this feed - * @return self - */ - public function setSelfLink($url) - { - return $this->setAtomLink($url, 'self', $this->getMIMEType()); - } - - /** - * Set the 'image' channel element - * - * @access public - * @param string $url URL of the image - * @param string $title Title of the image. RSS only. - * @param string $link Link target URL of the image. RSS only. - * @return self - * @throws \InvalidArgumentException if the url is invalid. - * @throws \InvalidArgumentException if the title and link parameter are not a string or empty. - */ - public function setImage($url, $title = null, $link = null) - { - if (!is_string($url) || empty($url)) - throw new \InvalidArgumentException('url parameter must be a string and may not be empty or NULL.'); - - // RSS feeds have support for a title & link element. - if ($this->version != Feed::ATOM) - { - if (!is_string($title) || empty($title)) - throw new \InvalidArgumentException('title parameter must be a string and may not be empty or NULL.'); - if (!is_string($link) || empty($link)) - throw new \InvalidArgumentException('link parameter must be a string and may not be empty or NULL.'); - - $data = array('title'=>$title, 'link'=>$link, 'url'=>$url); - $name = 'image'; - } - else - { - $name = 'logo'; - $data = $url; - } - - // Special handling for RSS1 again (since RSS1 is a bit strange...) - if ($this->version == Feed::RSS1) - { - $this->data['Image'] = $data; - return $this->setChannelElement($name, '', array('rdf:resource' => $url), false); - } - else - return $this->setChannelElement($name, $data); - } - - /** - * Set the channel 'rdf:about' attribute, which is used in RSS1 feeds only. - * - * @access public - * @param string $url value of 'rdf:about' attribute of the channel element - * @return self - * @throws InvalidOperationException if this method is called and the feed is not of type RSS1. - * @throws \InvalidArgumentException if the given URL is invalid. - */ - public function setChannelAbout($url) - { - if ($this->version != Feed::RSS1) - throw new InvalidOperationException("This method is only supported in RSS1 feeds."); - if (empty($url)) - throw new \InvalidArgumentException('The about URL may not be empty or NULL.'); - if (!is_string($url)) - throw new \InvalidArgumentException('The about URL must be a string.'); - - $this->data['ChannelAbout'] = $url; - - return $this; - } - - /** - * Generate an UUID. - * - * The UUID is based on an MD5 hash. If no key is given, a unique ID as the input - * for the MD5 hash is generated. - * - * @author Anis uddin Ahmad - * @access public - * @param string $key optional key on which the UUID is generated - * @param string $prefix an optional prefix - * @return string the formatted UUID - */ - public static function uuid($key = null, $prefix = '') - { - $key = ($key == null) ? uniqid(rand()) : $key; - $chars = md5($key); - $uuid = substr($chars,0,8) . '-'; - $uuid .= substr($chars,8,4) . '-'; - $uuid .= substr($chars,12,4) . '-'; - $uuid .= substr($chars,16,4) . '-'; - $uuid .= substr($chars,20,12); - - return $prefix . $uuid; - } - - /** - * Replace invalid XML characters. - * - * @link http://www.phpwact.org/php/i18n/charsets#xml See utf8_for_xml() function - * @link http://www.w3.org/TR/REC-xml/#charsets - * @link https://github.com/mibe/FeedWriter/issues/30 - * - * @access public - * @param string $string string which should be filtered - * @param string $replacement replace invalid characters with this string - * @return string the filtered string - */ - public static function filterInvalidXMLChars($string, $replacement = '_') // default to '\x{FFFD}' ??? - { - $result = preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u', $replacement, $string); - - // Did the PCRE replace failed because of bad UTF-8 data? - // If yes, try a non-multibyte regex and without the UTF-8 mode enabled. - if ($result == NULL && preg_last_error() == PREG_BAD_UTF8_ERROR) - $result = preg_replace('/[^\x09\x0a\x0d\x20-\xFF]+/', $replacement, $string); - - // In case the regex replacing failed completely, return the whole unfiltered string. - if ($result == NULL) - $result = $string; - - return $result; - } - // End # public functions ---------------------------------------------- - - // Start # private functions ---------------------------------------------- - - /** - * Returns all used XML namespace prefixes in this instance. - * This includes all channel elements and feed items. - * Unfortunately some namespace prefixes are not included, - * because they are hardcoded, e.g. rdf. - * - * @access private - * @return array Array with namespace prefix as value. - */ - private function getNamespacePrefixes() - { - $prefixes = array(); - - // Get all tag names from channel elements... - $tags = array_keys($this->channels); - - // ... and now all names from feed items - foreach ($this->items as $item) { - foreach (array_keys($item->getElements()) as $key) { - if (!in_array($key, $tags)) { - $tags[] = $key; - } - } - } - - // Look for prefixes in those tag names - foreach ($tags as $tag) { - $elements = explode(':', $tag); - - if (count($elements) != 2) - continue; - - $prefixes[] = $elements[0]; - } - - return array_unique($prefixes); - } - - /** - * Returns the XML header and root element, depending on the feed type. - * - * @access private - * @return string The XML header of the feed. - * @throws InvalidOperationException if an unknown XML namespace prefix is encountered. - */ - private function makeHeader() - { - $out = 'encoding.'" ?>' . PHP_EOL; - - $prefixes = $this->getNamespacePrefixes(); - $attributes = array(); - $tagName = ''; - $defaultNamespace = ''; - - if ($this->version == Feed::RSS2) { - $tagName = 'rss'; - $attributes['version'] = '2.0'; - } elseif ($this->version == Feed::RSS1) { - $tagName = 'rdf:RDF'; - $prefixes[] = 'rdf'; - $defaultNamespace = $this->namespaces['rss1']; - } elseif ($this->version == Feed::ATOM) { - $tagName = 'feed'; - $defaultNamespace = $this->namespaces['atom']; - - // Ugly hack to remove the 'atom' value from the prefixes array. - $prefixes = array_flip($prefixes); - unset($prefixes['atom']); - $prefixes = array_flip($prefixes); - } - - // Iterate through every namespace prefix and add it to the element attributes. - foreach ($prefixes as $prefix) { - if (!isset($this->namespaces[$prefix])) - throw new InvalidOperationException('Unknown XML namespace prefix: \'' . $prefix . '\'.' - . ' Use the addNamespace method to add support for this prefix.'); - else - $attributes['xmlns:' . $prefix] = $this->namespaces[$prefix]; - } - - // Include default namepsace, if required - if (!empty($defaultNamespace)) - $attributes['xmlns'] = $defaultNamespace; - - $out .= $this->makeNode($tagName, '', $attributes, true); - - return $out; - } - - /** - * Closes the open tags at the end of file - * - * @access private - * @return string The XML footer of the feed. - */ - private function makeFooter() - { - if ($this->version == Feed::RSS2) { - return '
' . PHP_EOL . ''; - } elseif ($this->version == Feed::RSS1) { - return ''; - } elseif ($this->version == Feed::ATOM) { - return ''; - } - } - - /** - * Creates a single node in XML format - * - * @access private - * @param string $tagName name of the tag - * @param mixed $tagContent tag value as string or array of nested tags in 'tagName' => 'tagValue' format - * @param array $attributes Attributes (if any) in 'attrName' => 'attrValue' format - * @param bool $omitEndTag True if the end tag should be omitted. Defaults to false. - * @return string formatted xml tag - * @throws \InvalidArgumentException if the tagContent is not an array and not a string. - */ - private function makeNode($tagName, $tagContent, array $attributes = null, $omitEndTag = false) - { - $nodeText = ''; - $attrText = ''; - - if ($attributes != null) { - foreach ($attributes as $key => $value) { - $value = self::filterInvalidXMLChars($value); - $value = htmlspecialchars($value); - $attrText .= " $key=\"$value\""; - } - } - - $attrText .= (in_array($tagName, $this->CDATAEncoding) && $this->version == Feed::ATOM) ? ' type="html"' : ''; - $nodeText .= "<{$tagName}{$attrText}>"; - $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? ' $value) { - if (is_array($value)) { - $nodeText .= PHP_EOL; - foreach ($value as $subValue) { - $nodeText .= $this->makeNode($key, $subValue); - } - } else if (is_string($value)) { - $nodeText .= $this->makeNode($key, $value); - } else { - throw new \InvalidArgumentException("Unknown node-value type for $key"); - } - } - } else { - $tagContent = self::filterInvalidXMLChars($tagContent); - $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? $this->sanitizeCDATA($tagContent) : htmlspecialchars($tagContent); - } - - $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? ']]>' : ''; - - if (!$omitEndTag) - $nodeText .= ""; - - $nodeText .= PHP_EOL; - - return $nodeText; - } - - /** - * Make the channels. - * - * @access private - * @return string The feed header as XML containing all the feed metadata. - */ - private function makeChannels() - { - $out = ''; - - //Start channel tag - switch ($this->version) { - case Feed::RSS2: - $out .= '' . PHP_EOL; - break; - case Feed::RSS1: - $out .= (isset($this->data['ChannelAbout']))? "data['ChannelAbout']}\">" : "channels['link']['content']}\">"; - break; - } - - //Print Items of channel - foreach ($this->channels as $key => $value) { - // In ATOM feeds, strip all ATOM namespace prefixes from the tag name. They are not needed here, - // because the ATOM namespace name is set as default namespace. - if ($this->version == Feed::ATOM && strncmp($key, 'atom', 4) == 0) { - $key = substr($key, 5); - } - - // The channel element can occur multiple times, when the key 'content' is not in the array. - if (!array_key_exists('content', $value)) { - // If this is the case, iterate through the array with the multiple elements. - foreach ($value as $singleElement) { - $out .= $this->makeNode($key, $singleElement['content'], $singleElement['attributes']); - } - } else { - $out .= $this->makeNode($key, $value['content'], $value['attributes']); - } - } - - if ($this->version == Feed::RSS1) { - //RSS 1.0 have special tag with channel - $out .= "" . PHP_EOL . "" . PHP_EOL; - foreach ($this->items as $item) { - $thisItems = $item->getElements(); - $out .= "" . PHP_EOL; - } - $out .= "" . PHP_EOL . "" . PHP_EOL . "" . PHP_EOL; - - // An image has its own element after the channel elements. - if (array_key_exists('image', $this->data)) - $out .= $this->makeNode('image', $this->data['Image'], array('rdf:about' => $this->data['Image']['url'])); - } else if ($this->version == Feed::ATOM) { - // ATOM feeds have a unique feed ID. Use the title channel element as key. - $out .= $this->makeNode('id', Feed::uuid($this->channels['title']['content'], 'urn:uuid:')); - } - - return $out; - } - - /** - * Prints formatted feed items - * - * @access private - * @return string The XML of every feed item. - */ - private function makeItems() - { - $out = ''; - - foreach ($this->items as $item) { - $thisItems = $item->getElements(); - - // The argument is printed as rdf:about attribute of item in RSS 1.0 - // We're using the link set in the item (which is mandatory) as the about attribute. - if ($this->version == Feed::RSS1) - $out .= $this->startItem($thisItems['link']['content']); - else - $out .= $this->startItem(); - - foreach ($thisItems as $feedItem) { - $name = $feedItem['name']; - - // Strip all ATOM namespace prefixes from tags when feed is an ATOM feed. - // Not needed here, because the ATOM namespace name is used as default namespace. - if ($this->version == Feed::ATOM && strncmp($name, 'atom', 4) == 0) - $name = substr($name, 5); - - $out .= $this->makeNode($name, $feedItem['content'], $feedItem['attributes']); - } - $out .= $this->endItem(); - } - - return $out; - } - - /** - * Make the starting tag of channels - * - * @access private - * @param string $about The value of about attribute which is used for RSS 1.0 only. - * @return string The starting XML tag of an feed item. - * @throws InvalidOperationException if this object misses the data for the about attribute. - */ - private function startItem($about = false) - { - $out = ''; - - if ($this->version == Feed::RSS2) { - $out .= '
' . PHP_EOL; - } elseif ($this->version == Feed::RSS1) { - if ($about) { - $out .= "" . PHP_EOL; - } else { - throw new InvalidOperationException("Missing data for about attribute. Call setChannelAbout method."); - } - } elseif ($this->version == Feed::ATOM) { - $out .= "" . PHP_EOL; - } - - return $out; - } - - /** - * Closes feed item tag - * - * @access private - * @return string The ending XML tag of an feed item. - */ - private function endItem() - { - if ($this->version == Feed::RSS2 || $this->version == Feed::RSS1) { - return '
' . PHP_EOL; - } elseif ($this->version == Feed::ATOM) { - return '' . PHP_EOL; - } - } - - /** - * Sanitizes data which will be later on returned as CDATA in the feed. - * - * A "]]>" respectively "", "]]>", $text); - $text = str_replace(" + * Copyright (C) 2010-2016 Michael Bemmerl + * + * 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 . + */ + +/** + * Universal Feed Writer class + * + * Generate RSS 1.0, RSS2.0 and ATOM Feeds + * + * @package UniversalFeedWriter + * @author Anis uddin Ahmad + * @link http://www.ajaxray.com/projects/rss + */ +abstract class Feed +{ + // RSS 0.90 Officially obsoleted by 1.0 + // RSS 0.91, 0.92, 0.93 and 0.94 Officially obsoleted by 2.0 + // So, define constants for RSS 1.0, RSS 2.0 and ATOM + + const RSS1 = 'RSS 1.0'; + const RSS2 = 'RSS 2.0'; + const ATOM = 'ATOM'; + + const VERSION = '1.1.0'; + + /** + * Collection of all channel elements + */ + private $channels = array(); + + /** + * Collection of items as object of \FeedWriter\Item class. + */ + private $items = array(); + + /** + * Collection of other version wise data. + * + * Currently used to store the 'rdf:about' attribute and image element of the channel (both RSS1 only). + */ + private $data = array(); + + /** + * The tag names which have to encoded as CDATA + */ + private $CDATAEncoding = array(); + + /** + * Collection of XML namespaces + */ + private $namespaces = array(); + + /** + * Contains the format of this feed. + */ + private $version = null; + + /** + * Constructor + * + * If no version is given, a feed in RSS 2.0 format will be generated. + * + * @param string $version the version constant (RSS1/RSS2/ATOM). + */ + protected function __construct($version = Feed::RSS2) + { + $this->version = $version; + + // Setting default encoding + $this->encoding = 'utf-8'; + + // Setting default value for essential channel element + $this->setTitle($version . ' Feed'); + + // Add some default XML namespaces + $this->namespaces['content'] = 'http://purl.org/rss/1.0/modules/content/'; + $this->namespaces['wfw'] = 'http://wellformedweb.org/CommentAPI/'; + $this->namespaces['atom'] = 'http://www.w3.org/2005/Atom'; + $this->namespaces['rdf'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; + $this->namespaces['rss1'] = 'http://purl.org/rss/1.0/'; + $this->namespaces['dc'] = 'http://purl.org/dc/elements/1.1/'; + $this->namespaces['sy'] = 'http://purl.org/rss/1.0/modules/syndication/'; + + // Tag names to encode in CDATA + $this->addCDATAEncoding(array('description', 'content:encoded', 'summary')); + } + + // Start # public functions --------------------------------------------- + + /** + * Set the URLs for feed pagination. + * + * See RFC 5005, chapter 3. At least one page URL must be specified. + * + * @param string $nextURL The URL to the next page of this feed. Optional. + * @param string $previousURL The URL to the previous page of this feed. Optional. + * @param string $firstURL The URL to the first page of this feed. Optional. + * @param string $lastURL The URL to the last page of this feed. Optional. + * @link http://tools.ietf.org/html/rfc5005#section-3 + * @return self + * @throws \LogicException if none of the parameters are set. + */ + public function setPagination($nextURL = null, $previousURL = null, $firstURL = null, $lastURL = null) + { + if (empty($nextURL) && empty($previousURL) && empty($firstURL) && empty($lastURL)) + throw new \LogicException('At least one URL must be specified for pagination to work.'); + + if (!empty($nextURL)) + $this->setAtomLink($nextURL, 'next'); + + if (!empty($previousURL)) + $this->setAtomLink($previousURL, 'previous'); + + if (!empty($firstURL)) + $this->setAtomLink($firstURL, 'first'); + + if (!empty($lastURL)) + $this->setAtomLink($lastURL, 'last'); + + return $this; + } + + /** + * Add a channel element indicating the program used to generate the feed. + * + * @return self + * @throws InvalidOperationException if this method is called on an RSS1 feed. + */ + public function addGenerator() + { + if ($this->version == Feed::ATOM) + $this->setChannelElement('atom:generator', 'FeedWriter', array('uri' => 'https://github.com/mibe/FeedWriter')); + else if ($this->version == Feed::RSS2) + $this->setChannelElement('generator', 'FeedWriter'); + else + throw new InvalidOperationException('The generator element is not supported in RSS1 feeds.'); + + return $this; + } + + /** + * Add a XML namespace to the internal list of namespaces. After that, + * custom channel elements can be used properly to generate a valid feed. + * + * @access public + * @param string $prefix namespace prefix + * @param string $uri namespace name (URI) + * @return self + * @link http://www.w3.org/TR/REC-xml-names/ + * @throws \InvalidArgumentException if the prefix or uri is empty or NULL. + */ + public function addNamespace($prefix, $uri) + { + if (empty($prefix)) + throw new \InvalidArgumentException('The prefix may not be emtpy or NULL.'); + if (empty($uri)) + throw new \InvalidArgumentException('The uri may not be empty or NULL.'); + + $this->namespaces[$prefix] = $uri; + + return $this; + } + + /** + * Add a channel element to the feed. + * + * @access public + * @param string $elementName name of the channel tag + * @param string $content content of the channel tag + * @param array array of element attributes with attribute name as array key + * @param bool TRUE if this element can appear multiple times + * @return self + * @throws \InvalidArgumentException if the element name is not a string, empty or NULL. + */ + public function setChannelElement($elementName, $content, array $attributes = null, $multiple = 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.'); + + $entity['content'] = $content; + $entity['attributes'] = $attributes; + + if ($multiple === TRUE) + $this->channels[$elementName][] = $entity; + else + $this->channels[$elementName] = $entity; + + return $this; + } + + /** + * Set multiple channel elements from an array. Array elements + * should be 'channelName' => 'channelContent' format. + * + * @access public + * @param array array of channels + * @return self + */ + public function setChannelElementsFromArray(array $elementArray) + { + foreach ($elementArray as $elementName => $content) { + $this->setChannelElement($elementName, $content); + } + + return $this; + } + + /** + * Get the appropriate MIME type string for the current feed. + * + * @access public + * @return string The MIME type string. + */ + public function getMIMEType() + { + switch ($this->version) { + case Feed::RSS2 : $mimeType = "application/rss+xml"; + break; + case Feed::RSS1 : $mimeType = "application/rdf+xml"; + break; + case Feed::ATOM : $mimeType = "application/atom+xml"; + break; + default : $mimeType = "text/xml"; + } + + return $mimeType; + } + + /** + * Print the actual RSS/ATOM file + * + * Sets a Content-Type header and echoes the contents of the feed. + * Should only be used in situations where direct output is desired; + * if you need to pass a string around, use generateFeed() instead. + * + * @access public + * @param bool FALSE if the specific feed media type should be sent. + * @return void + * @throws \InvalidArgumentException if the useGenericContentType parameter is not boolean. + */ + public function printFeed($useGenericContentType = false) + { + if (!is_bool($useGenericContentType)) + throw new \InvalidArgumentException('The useGenericContentType parameter must be boolean.'); + + $contentType = "text/xml"; + + if (!$useGenericContentType) { + $contentType = $this->getMIMEType(); + } + + // Generate the feed before setting the header, so Exceptions will be nicely visible. + $feed = $this->generateFeed(); + header("Content-Type: " . $contentType . "; charset=" . $this->encoding); + echo $feed; + } + + /** + * Generate the feed. + * + * @access public + * @return string The complete feed XML. + * @throws InvalidOperationException if the link element of the feed is not set. + */ + public function generateFeed() + { + if ($this->version != Feed::ATOM && !array_key_exists('link', $this->channels)) + throw new InvalidOperationException('RSS1 & RSS2 feeds need a link element. Call the setLink method before this method.'); + + return $this->makeHeader() + . $this->makeChannels() + . $this->makeItems() + . $this->makeFooter(); + } + + /** + * Create a new Item. + * + * @access public + * @return Item instance of Item class + */ + public function createNewItem() + { + $Item = new Item($this->version); + + return $Item; + } + + /** + * Add one or more tags to the list of CDATA encoded tags + * + * @access public + * @param array $tags An array of tag names that are merged into the list of tags which should be encoded as CDATA + * @return self + */ + public function addCDATAEncoding(array $tags) + { + $this->CDATAEncoding = array_merge($this->CDATAEncoding, $tags); + + return $this; + } + + /** + * Get list of CDATA encoded properties + * + * @access public + * @return array Return an array of CDATA properties that are to be encoded as CDATA + */ + public function getCDATAEncoding() + { + return $this->CDATAEncoding; + } + + /** + * Remove tags from the list of CDATA encoded tags + * + * @access public + * @param array $tags An array of tag names that should be removed. + * @return void + */ + public function removeCDATAEncoding(array $tags) + { + // Call array_values to re-index the array. + $this->CDATAEncoding = array_values(array_diff($this->CDATAEncoding, $tags)); + } + + /** + * Add a FeedItem to the main class + * + * @access public + * @param Item $feedItem instance of Item class + * @return self + * @throws \InvalidArgumentException if the given item version mismatches. + */ + public function addItem(Item $feedItem) + { + if ($feedItem->getVersion() != $this->version) + { + $msg = sprintf('Feed type mismatch: This instance can handle %s feeds only, but item for %s feeds given.', $this->version, $feedItem->getVersion()); + throw new \InvalidArgumentException($msg); + } + + $this->items[] = $feedItem; + + return $this; + } + + // Wrapper functions ------------------------------------------------------------------- + + /** + * Set the 'encoding' attribute in the XML prolog. + * + * @access public + * @param string $encoding value of 'encoding' attribute + * @return self + * @throws \InvalidArgumentException if the encoding is not a string, empty or NULL. + */ + public function setEncoding($encoding) + { + if (empty($encoding)) + throw new \InvalidArgumentException('The encoding may not be empty or NULL.'); + if (!is_string($encoding)) + throw new \InvalidArgumentException('The encoding must be a string.'); + + $this->encoding = $encoding; + + return $this; + } + + /** + * Set the 'title' channel element + * + * @access public + * @param string $title value of 'title' channel tag + * @return self + */ + public function setTitle($title) + { + return $this->setChannelElement('title', $title); + } + + /** + * Set the date when the feed was lastly updated. + * + * This adds the 'updated' element to the feed. The value of the date parameter + * can be either an instance of the DateTime class, an integer containing a UNIX + * timestamp or a string which is parseable by PHP's 'strtotime' function. + * + * Not supported in RSS1 feeds. + * + * @access public + * @param DateTime|int|string Date which should be used. + * @return self + * @throws \InvalidArgumentException if the given date is not an instance of DateTime, a UNIX timestamp or a date string. + * @throws InvalidOperationException if this method is called on an RSS1 feed. + */ + public function setDate($date) + { + if ($this->version == Feed::RSS1) + throw new InvalidOperationException('The publication date is not supported in RSS1 feeds.'); + + // The feeds have different date formats. + $format = $this->version == Feed::ATOM ? \DATE_ATOM : \DATE_RSS; + + if ($date instanceof DateTime) + $date = $date->format($format); + else if(is_numeric($date) && $date >= 0) + $date = date($format, $date); + else if (is_string($date)) + { + $timestamp = strtotime($date); + if ($timestamp === FALSE) + throw new \InvalidArgumentException('The given date was not parseable.'); + + $date = date($format, $timestamp); + } + else + throw new \InvalidArgumentException('The given date is not an instance of DateTime, a UNIX timestamp or a date string.'); + + if ($this->version == Feed::ATOM) + $this->setChannelElement('updated', $date); + else + $this->setChannelElement('lastBuildDate', $date); + + return $this; + } + + /** + * Set a phrase or sentence describing the feed. + * + * @access public + * @param string $description Description of the feed. + * @return self + */ + public function setDescription($description) + { + if ($this->version != Feed::ATOM) + $this->setChannelElement('description', $description); + else + $this->setChannelElement('subtitle', $description); + + return $this; + } + + /** + * Set the 'link' channel element + * + * @access public + * @param string $link value of 'link' channel tag + * @return self + */ + public function setLink($link) + { + if ($this->version == Feed::ATOM) + $this->setAtomLink($link); + else + $this->setChannelElement('link', $link); + + return $this; + } + + /** + * Set custom 'link' channel elements. + * + * In ATOM feeds, only one link with alternate relation and the same combination of + * type and hreflang values. + * + * @access public + * @param string $href URI of this link + * @param string $rel relation type of the resource + * @param string $type MIME type of the target resource + * @param string $hreflang language of the resource + * @param string $title human-readable information about the resource + * @param int $length length of the resource in bytes + * @link https://www.iana.org/assignments/link-relations/link-relations.xml + * @link https://tools.ietf.org/html/rfc4287#section-4.2.7 + * @return self + * @throws \InvalidArgumentException on multiple occasions. + * @throws InvalidOperationException if the same link with the same attributes was already added to the feed. + */ + public function setAtomLink($href, $rel = null, $type = null, $hreflang = null, $title = null, $length = null) + { + $data = array('href' => $href); + + if ($rel != null) { + if (!is_string($rel) || empty($rel)) + throw new \InvalidArgumentException('rel parameter must be a string and a valid relation identifier.'); + + $data['rel'] = $rel; + } + if ($type != null) { + // 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.'); + + $data['type'] = $type; + } + if ($hreflang != null) { + // Regex used from RFC 4287, page 41 + if (!is_string($hreflang) || preg_match('/[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*/', $hreflang) != 1) + throw new \InvalidArgumentException('hreflang parameter must be a string and a valid language code.'); + + $data['hreflang'] = $hreflang; + } + if ($title != null) { + if (!is_string($title) || empty($title)) + throw new \InvalidArgumentException('title parameter must be a string and not empty.'); + + $data['title'] = $title; + } + if ($length != null) { + if (!is_int($length) || $length < 0) + throw new \InvalidArgumentException('length parameter must be a positive integer.'); + + $data['length'] = (string) $length; + } + + // ATOM spec. has some restrictions on atom:link usage + // See RFC 4287, page 12 (4.1.1) + if ($this->version == Feed::ATOM) { + foreach ($this->channels as $key => $value) { + if ($key != 'atom:link') + continue; + + // $value is an array , so check every element + foreach ($value as $linkItem) { + $attrib = $linkItem['attributes']; + // Only one link with relation alternate and same hreflang & type is allowed. + if (@$attrib['rel'] == 'alternate' && @$attrib['hreflang'] == $hreflang && @$attrib['type'] == $type) + throw new InvalidOperationException('The feed must not contain more than one link element with a' + . ' relation of "alternate" that has the same combination of type and hreflang attribute values.'); + } + } + } + + return $this->setChannelElement('atom:link', '', $data, true); + } + + /** + * Set an 'atom:link' channel element with relation=self attribute. + * Needs the full URL to this feed. + * + * @link http://www.rssboard.org/rss-profile#namespace-elements-atom-link + * @access public + * @param string $url URL to this feed + * @return self + */ + public function setSelfLink($url) + { + return $this->setAtomLink($url, 'self', $this->getMIMEType()); + } + + /** + * Set the 'image' channel element + * + * @access public + * @param string $url URL of the image + * @param string $title Title of the image. RSS only. + * @param string $link Link target URL of the image. RSS only. + * @return self + * @throws \InvalidArgumentException if the url is invalid. + * @throws \InvalidArgumentException if the title and link parameter are not a string or empty. + */ + public function setImage($url, $title = null, $link = null) + { + if (!is_string($url) || empty($url)) + throw new \InvalidArgumentException('url parameter must be a string and may not be empty or NULL.'); + + // RSS feeds have support for a title & link element. + if ($this->version != Feed::ATOM) + { + if (!is_string($title) || empty($title)) + throw new \InvalidArgumentException('title parameter must be a string and may not be empty or NULL.'); + if (!is_string($link) || empty($link)) + throw new \InvalidArgumentException('link parameter must be a string and may not be empty or NULL.'); + + $data = array('title'=>$title, 'link'=>$link, 'url'=>$url); + $name = 'image'; + } + else + { + $name = 'logo'; + $data = $url; + } + + // Special handling for RSS1 again (since RSS1 is a bit strange...) + if ($this->version == Feed::RSS1) + { + $this->data['Image'] = $data; + return $this->setChannelElement($name, '', array('rdf:resource' => $url), false); + } + else + return $this->setChannelElement($name, $data); + } + + /** + * Set the channel 'rdf:about' attribute, which is used in RSS1 feeds only. + * + * @access public + * @param string $url value of 'rdf:about' attribute of the channel element + * @return self + * @throws InvalidOperationException if this method is called and the feed is not of type RSS1. + * @throws \InvalidArgumentException if the given URL is invalid. + */ + public function setChannelAbout($url) + { + if ($this->version != Feed::RSS1) + throw new InvalidOperationException("This method is only supported in RSS1 feeds."); + if (empty($url)) + throw new \InvalidArgumentException('The about URL may not be empty or NULL.'); + if (!is_string($url)) + throw new \InvalidArgumentException('The about URL must be a string.'); + + $this->data['ChannelAbout'] = $url; + + return $this; + } + + /** + * Generate an UUID. + * + * The UUID is based on an MD5 hash. If no key is given, a unique ID as the input + * for the MD5 hash is generated. + * + * @author Anis uddin Ahmad + * @access public + * @param string $key optional key on which the UUID is generated + * @param string $prefix an optional prefix + * @return string the formatted UUID + */ + public static function uuid($key = null, $prefix = '') + { + $key = ($key == null) ? uniqid(rand()) : $key; + $chars = md5($key); + $uuid = substr($chars,0,8) . '-'; + $uuid .= substr($chars,8,4) . '-'; + $uuid .= substr($chars,12,4) . '-'; + $uuid .= substr($chars,16,4) . '-'; + $uuid .= substr($chars,20,12); + + return $prefix . $uuid; + } + + /** + * Replace invalid XML characters. + * + * @link http://www.phpwact.org/php/i18n/charsets#xml See utf8_for_xml() function + * @link http://www.w3.org/TR/REC-xml/#charsets + * @link https://github.com/mibe/FeedWriter/issues/30 + * + * @access public + * @param string $string string which should be filtered + * @param string $replacement replace invalid characters with this string + * @return string the filtered string + */ + public static function filterInvalidXMLChars($string, $replacement = '_') // default to '\x{FFFD}' ??? + { + $result = preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u', $replacement, $string); + + // Did the PCRE replace failed because of bad UTF-8 data? + // If yes, try a non-multibyte regex and without the UTF-8 mode enabled. + if ($result == NULL && preg_last_error() == PREG_BAD_UTF8_ERROR) + $result = preg_replace('/[^\x09\x0a\x0d\x20-\xFF]+/', $replacement, $string); + + // In case the regex replacing failed completely, return the whole unfiltered string. + if ($result == NULL) + $result = $string; + + return $result; + } + // End # public functions ---------------------------------------------- + + // Start # private functions ---------------------------------------------- + + /** + * Returns all used XML namespace prefixes in this instance. + * This includes all channel elements and feed items. + * Unfortunately some namespace prefixes are not included, + * because they are hardcoded, e.g. rdf. + * + * @access private + * @return array Array with namespace prefix as value. + */ + private function getNamespacePrefixes() + { + $prefixes = array(); + + // Get all tag names from channel elements... + $tags = array_keys($this->channels); + + // ... and now all names from feed items + foreach ($this->items as $item) { + foreach (array_keys($item->getElements()) as $key) { + if (!in_array($key, $tags)) { + $tags[] = $key; + } + } + } + + // Look for prefixes in those tag names + foreach ($tags as $tag) { + $elements = explode(':', $tag); + + if (count($elements) != 2) + continue; + + $prefixes[] = $elements[0]; + } + + return array_unique($prefixes); + } + + /** + * Returns the XML header and root element, depending on the feed type. + * + * @access private + * @return string The XML header of the feed. + * @throws InvalidOperationException if an unknown XML namespace prefix is encountered. + */ + private function makeHeader() + { + $out = 'encoding.'" ?>' . PHP_EOL; + + $prefixes = $this->getNamespacePrefixes(); + $attributes = array(); + $tagName = ''; + $defaultNamespace = ''; + + if ($this->version == Feed::RSS2) { + $tagName = 'rss'; + $attributes['version'] = '2.0'; + } elseif ($this->version == Feed::RSS1) { + $tagName = 'rdf:RDF'; + $prefixes[] = 'rdf'; + $defaultNamespace = $this->namespaces['rss1']; + } elseif ($this->version == Feed::ATOM) { + $tagName = 'feed'; + $defaultNamespace = $this->namespaces['atom']; + + // Ugly hack to remove the 'atom' value from the prefixes array. + $prefixes = array_flip($prefixes); + unset($prefixes['atom']); + $prefixes = array_flip($prefixes); + } + + // Iterate through every namespace prefix and add it to the element attributes. + foreach ($prefixes as $prefix) { + if (!isset($this->namespaces[$prefix])) + throw new InvalidOperationException('Unknown XML namespace prefix: \'' . $prefix . '\'.' + . ' Use the addNamespace method to add support for this prefix.'); + else + $attributes['xmlns:' . $prefix] = $this->namespaces[$prefix]; + } + + // Include default namepsace, if required + if (!empty($defaultNamespace)) + $attributes['xmlns'] = $defaultNamespace; + + $out .= $this->makeNode($tagName, '', $attributes, true); + + return $out; + } + + /** + * Closes the open tags at the end of file + * + * @access private + * @return string The XML footer of the feed. + */ + private function makeFooter() + { + if ($this->version == Feed::RSS2) { + return '
' . PHP_EOL . ''; + } elseif ($this->version == Feed::RSS1) { + return ''; + } elseif ($this->version == Feed::ATOM) { + return ''; + } + } + + /** + * Creates a single node in XML format + * + * @access private + * @param string $tagName name of the tag + * @param mixed $tagContent tag value as string or array of nested tags in 'tagName' => 'tagValue' format + * @param array $attributes Attributes (if any) in 'attrName' => 'attrValue' format + * @param bool $omitEndTag True if the end tag should be omitted. Defaults to false. + * @return string formatted xml tag + * @throws \InvalidArgumentException if the tagContent is not an array and not a string. + */ + private function makeNode($tagName, $tagContent, array $attributes = null, $omitEndTag = false) + { + $nodeText = ''; + $attrText = ''; + + if ($attributes != null) { + foreach ($attributes as $key => $value) { + $value = self::filterInvalidXMLChars($value); + $value = htmlspecialchars($value); + $attrText .= " $key=\"$value\""; + } + } + + $attrText .= (in_array($tagName, $this->CDATAEncoding) && $this->version == Feed::ATOM) ? ' type="html"' : ''; + $nodeText .= "<{$tagName}{$attrText}>"; + $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? ' $value) { + if (is_array($value)) { + $nodeText .= PHP_EOL; + foreach ($value as $subValue) { + $nodeText .= $this->makeNode($key, $subValue); + } + } else if (is_string($value)) { + $nodeText .= $this->makeNode($key, $value); + } else { + throw new \InvalidArgumentException("Unknown node-value type for $key"); + } + } + } else { + $tagContent = self::filterInvalidXMLChars($tagContent); + $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? $this->sanitizeCDATA($tagContent) : htmlspecialchars($tagContent); + } + + $nodeText .= (in_array($tagName, $this->CDATAEncoding)) ? ']]>' : ''; + + if (!$omitEndTag) + $nodeText .= ""; + + $nodeText .= PHP_EOL; + + return $nodeText; + } + + /** + * Make the channels. + * + * @access private + * @return string The feed header as XML containing all the feed metadata. + */ + private function makeChannels() + { + $out = ''; + + //Start channel tag + switch ($this->version) { + case Feed::RSS2: + $out .= '' . PHP_EOL; + break; + case Feed::RSS1: + $out .= (isset($this->data['ChannelAbout']))? "data['ChannelAbout']}\">" : "channels['link']['content']}\">"; + break; + } + + //Print Items of channel + foreach ($this->channels as $key => $value) { + // In ATOM feeds, strip all ATOM namespace prefixes from the tag name. They are not needed here, + // because the ATOM namespace name is set as default namespace. + if ($this->version == Feed::ATOM && strncmp($key, 'atom', 4) == 0) { + $key = substr($key, 5); + } + + // The channel element can occur multiple times, when the key 'content' is not in the array. + if (!array_key_exists('content', $value)) { + // If this is the case, iterate through the array with the multiple elements. + foreach ($value as $singleElement) { + $out .= $this->makeNode($key, $singleElement['content'], $singleElement['attributes']); + } + } else { + $out .= $this->makeNode($key, $value['content'], $value['attributes']); + } + } + + if ($this->version == Feed::RSS1) { + //RSS 1.0 have special tag with channel + $out .= "" . PHP_EOL . "" . PHP_EOL; + foreach ($this->items as $item) { + $thisItems = $item->getElements(); + $out .= "" . PHP_EOL; + } + $out .= "" . PHP_EOL . "" . PHP_EOL . "" . PHP_EOL; + + // An image has its own element after the channel elements. + if (array_key_exists('image', $this->data)) + $out .= $this->makeNode('image', $this->data['Image'], array('rdf:about' => $this->data['Image']['url'])); + } else if ($this->version == Feed::ATOM) { + // ATOM feeds have a unique feed ID. Use the title channel element as key. + $out .= $this->makeNode('id', Feed::uuid($this->channels['title']['content'], 'urn:uuid:')); + } + + return $out; + } + + /** + * Prints formatted feed items + * + * @access private + * @return string The XML of every feed item. + */ + private function makeItems() + { + $out = ''; + + foreach ($this->items as $item) { + $thisItems = $item->getElements(); + + // The argument is printed as rdf:about attribute of item in RSS 1.0 + // We're using the link set in the item (which is mandatory) as the about attribute. + if ($this->version == Feed::RSS1) + $out .= $this->startItem($thisItems['link']['content']); + else + $out .= $this->startItem(); + + foreach ($thisItems as $feedItem) { + $name = $feedItem['name']; + + // Strip all ATOM namespace prefixes from tags when feed is an ATOM feed. + // Not needed here, because the ATOM namespace name is used as default namespace. + if ($this->version == Feed::ATOM && strncmp($name, 'atom', 4) == 0) + $name = substr($name, 5); + + $out .= $this->makeNode($name, $feedItem['content'], $feedItem['attributes']); + } + $out .= $this->endItem(); + } + + return $out; + } + + /** + * Make the starting tag of channels + * + * @access private + * @param string $about The value of about attribute which is used for RSS 1.0 only. + * @return string The starting XML tag of an feed item. + * @throws InvalidOperationException if this object misses the data for the about attribute. + */ + private function startItem($about = false) + { + $out = ''; + + if ($this->version == Feed::RSS2) { + $out .= '
' . PHP_EOL; + } elseif ($this->version == Feed::RSS1) { + if ($about) { + $out .= "" . PHP_EOL; + } else { + throw new InvalidOperationException("Missing data for about attribute. Call setChannelAbout method."); + } + } elseif ($this->version == Feed::ATOM) { + $out .= "" . PHP_EOL; + } + + return $out; + } + + /** + * Closes feed item tag + * + * @access private + * @return string The ending XML tag of an feed item. + */ + private function endItem() + { + if ($this->version == Feed::RSS2 || $this->version == Feed::RSS1) { + return '
' . PHP_EOL; + } elseif ($this->version == Feed::ATOM) { + return '' . PHP_EOL; + } + } + + /** + * Sanitizes data which will be later on returned as CDATA in the feed. + * + * A "]]>" respectively "", "]]>", $text); + $text = str_replace(" - * - * 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 . - */ - -/** - * The exception that is thrown when an invalid operation is performed on - * the object. - * - * @package UniversalFeedWriter - */ -class InvalidOperationException extends LogicException -{ -} + + * + * 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 . + */ + +/** + * The exception that is thrown when an invalid operation is performed on + * the object. + * + * @package UniversalFeedWriter + */ +class InvalidOperationException extends LogicException +{ +} diff --git a/module/download/vendor/FeedWriter/Item.php b/module/download/vendor/FeedWriter/Item.php index c4f21a93..12b7f9c0 100644 --- a/module/download/vendor/FeedWriter/Item.php +++ b/module/download/vendor/FeedWriter/Item.php @@ -1,413 +1,413 @@ - - * Copyright (C) 2010-2013, 2015-2016 Michael Bemmerl - * - * 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 . - */ - -/** - * Universal Feed Writer - * - * Item class - Used as feed element in Feed class - * - * @package UniversalFeedWriter - * @author Anis uddin Ahmad - * @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 download 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 an instance of the - * DateTime class, an integer containing a UNIX timestamp or a string - * which is parseable by PHP's 'strtotime' function. - * - * @access public - * @param DateTime|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 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 4288 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/rfc4288 - * @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', 'cap', 'cid', 'coap', 'coaps', 'crid', 'data', 'dav', 'dict', 'dns', 'example', 'fax', 'file', 'filesystem', 'ftp', 'geo', 'go', 'gopher', 'h323', 'http', 'https', 'iax', 'icap', 'im', 'imap', 'info', 'ipp', 'ipps', 'iris', 'iris.beep', 'iris.lwz', 'iris.xpc', 'iris.xpcs', 'jabber', 'ldap', 'mailserver', 'mailto', 'mid', 'modem', 'msrp', 'msrps', 'mtqp', 'mupdate', 'news', 'nfs', 'ni', 'nih', 'nntp', 'opaquelocktoken', 'pack', 'pkcs11', 'pop', 'pres', 'prospero', 'reload', 'rtsp', 'rtsps', 'rtspu', 'service', 'session', 'shttp', 'sieve', 'sip', 'sips', 'sms', 'snews', 'snmp', 'soap.beep', 'soap.beeps', 'stun', 'stuns', 'tag', 'tel', 'telnet', 'tftp', 'thismessage', 'tip', 'tn3270', 'turn', 'turns', 'tv', 'urn', 'vemmi', 'videotex', 'vnc', 'wais', '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 + + * Copyright (C) 2010-2013, 2015-2016 Michael Bemmerl + * + * 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 . + */ + +/** + * Universal Feed Writer + * + * Item class - Used as feed element in Feed class + * + * @package UniversalFeedWriter + * @author Anis uddin Ahmad + * @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 download 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 an instance of the + * DateTime class, an integer containing a UNIX timestamp or a string + * which is parseable by PHP's 'strtotime' function. + * + * @access public + * @param DateTime|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 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 4288 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/rfc4288 + * @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', 'cap', 'cid', 'coap', 'coaps', 'crid', 'data', 'dav', 'dict', 'dns', 'example', 'fax', 'file', 'filesystem', 'ftp', 'geo', 'go', 'gopher', 'h323', 'http', 'https', 'iax', 'icap', 'im', 'imap', 'info', 'ipp', 'ipps', 'iris', 'iris.beep', 'iris.lwz', 'iris.xpc', 'iris.xpcs', 'jabber', 'ldap', 'mailserver', 'mailto', 'mid', 'modem', 'msrp', 'msrps', 'mtqp', 'mupdate', 'news', 'nfs', 'ni', 'nih', 'nntp', 'opaquelocktoken', 'pack', 'pkcs11', 'pop', 'pres', 'prospero', 'reload', 'rtsp', 'rtsps', 'rtspu', 'service', 'session', 'shttp', 'sieve', 'sip', 'sips', 'sms', 'snews', 'snmp', 'soap.beep', 'soap.beeps', 'stun', 'stuns', 'tag', 'tel', 'telnet', 'tftp', 'thismessage', 'tip', 'tn3270', 'turn', 'turns', 'tv', 'urn', 'vemmi', 'videotex', 'vnc', 'wais', '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 diff --git a/module/download/vendor/FeedWriter/README.md b/module/download/vendor/FeedWriter/README.md index f630af99..5d1d1fba 100644 --- a/module/download/vendor/FeedWriter/README.md +++ b/module/download/vendor/FeedWriter/README.md @@ -1,42 +1,42 @@ -# 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. - -## 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 +# 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. + +## 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 diff --git a/module/download/vendor/FeedWriter/RSS1.php b/module/download/vendor/FeedWriter/RSS1.php index a0465cf5..69ac1874 100644 --- a/module/download/vendor/FeedWriter/RSS1.php +++ b/module/download/vendor/FeedWriter/RSS1.php @@ -1,37 +1,37 @@ - - * - * 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 . - */ - -/** - * Wrapper for creating RSS1 feeds - * - * @package UniversalFeedWriter - */ -class RSS1 extends Feed -{ - /** - * {@inheritdoc} - */ - public function __construct() - { - parent::__construct(Feed::RSS1); - } -} + + * + * 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 . + */ + +/** + * Wrapper for creating RSS1 feeds + * + * @package UniversalFeedWriter + */ +class RSS1 extends Feed +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct(Feed::RSS1); + } +} diff --git a/module/download/vendor/FeedWriter/RSS2.php b/module/download/vendor/FeedWriter/RSS2.php index 9e36a728..3fa7db8d 100644 --- a/module/download/vendor/FeedWriter/RSS2.php +++ b/module/download/vendor/FeedWriter/RSS2.php @@ -1,37 +1,37 @@ - - * - * 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 . - */ - -/** - * Wrapper for creating RSS2 feeds - * - * @package UniversalFeedWriter - */ -class RSS2 extends Feed -{ - /** - * {@inheritdoc} - */ - public function __construct() - { - parent::__construct(Feed::RSS2); - } -} + + * + * 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 . + */ + +/** + * Wrapper for creating RSS2 feeds + * + * @package UniversalFeedWriter + */ +class RSS2 extends Feed +{ + /** + * {@inheritdoc} + */ + public function __construct() + { + parent::__construct(Feed::RSS2); + } +} diff --git a/module/download/view/add/add.css b/module/download/view/add/add.css index 805e9150..dc0024c0 100644 --- a/module/download/view/add/add.css +++ b/module/download/view/add/add.css @@ -1,18 +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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @author Frédéric Tempez - * @copyright Copyright (C) 2018-2020, Frédéric Tempez - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - - -/** NE PAS EFFACER -* admin.css +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css */ \ No newline at end of file diff --git a/module/download/view/add/add.js.php b/module/download/view/add/add.js.php index d88ff563..06cb3b0b 100644 --- a/module/download/view/add/add.js.php +++ b/module/download/view/add/add.js.php @@ -1,54 +1,54 @@ -/** - * 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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - -/** - * Soumission du formulaire pour enregistrer en brouillon - */ -$("#downloadAddDraft").on("click", function() { - $("#downloadAddState").val(0); - $("#downloadAddForm").trigger("submit"); -}); - -/** - * Options de commentaires - */ -$("#downloadAddCommentClose").on("change", function() { - if ($(this).is(':checked') ) { - $(".commentOptionsWrapper").slideUp(); - } else { - $(".commentOptionsWrapper").slideDown(); - } -}); - -$("#downloadAddCommentNotification").on("change", function() { - if ($(this).is(':checked') ) { - $("#downloadAddCommentGroupNotification").slideDown(); - } else { - $("#downloadAddCommentGroupNotification").slideUp(); - } -}); - - -$( document).ready(function() { - - if ($("#downloadAddCloseComment").is(':checked') ) { - $(".commentOptionsWrapper").slideUp(); - } else { - $(".commentOptionsWrapper").slideDown(); - } - - if ($("#downloadAddCommentNotification").is(':checked') ) { - $("#downloadAddCommentGroupNotification").slideDown(); - } else { - $("#downloadAddCommentGroupNotification").slideUp(); - } +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +/** + * Soumission du formulaire pour enregistrer en brouillon + */ +$("#downloadAddDraft").on("click", function() { + $("#downloadAddState").val(0); + $("#downloadAddForm").trigger("submit"); +}); + +/** + * Options de commentaires + */ +$("#downloadAddCommentClose").on("change", function() { + if ($(this).is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } +}); + +$("#downloadAddCommentNotification").on("change", function() { + if ($(this).is(':checked') ) { + $("#downloadAddCommentGroupNotification").slideDown(); + } else { + $("#downloadAddCommentGroupNotification").slideUp(); + } +}); + + +$( document).ready(function() { + + if ($("#downloadAddCloseComment").is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } + + if ($("#downloadAddCommentNotification").is(':checked') ) { + $("#downloadAddCommentGroupNotification").slideDown(); + } else { + $("#downloadAddCommentGroupNotification").slideUp(); + } }); \ No newline at end of file diff --git a/module/download/view/add/add.php b/module/download/view/add/add.php index e75215af..a4df7f10 100644 --- a/module/download/view/add/add.php +++ b/module/download/view/add/add.php @@ -1,125 +1,125 @@ - -
-
- 'buttonGrey', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'ico' => 'left', - 'value' => 'Retour' - ]); ?> -
-
- true, - 'value' => 'Enregistrer en brouillon' - ]); ?> - true - ]); ?> -
-
- 'Publier' - ]); ?> -
-
-
-
-
-

Informations générales

-
-
- 'Titre' - ]); ?> -
-
- 'Version' - ]); ?> -
-
- 'Date' - ]); ?> -
-
-
-
- 'Archive du fichier' - ]); ?> -
-
- 'Capture d\'écran', - 'type' => 1 - ]); ?> -
-
-
-
-
- 'editorWysiwyg' - ]); ?> -
-
-
-

Options de publication

-
-
- 'Auteur', - 'selected' => $this->getUser('id'), - 'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false - ]); ?> -
-
- 'L\'item n\'est visible qu\'après la date de publication prévue.', - 'label' => 'Date de publication', - 'value' => time() - ]); ?> -
-
- 'Edition / Suppression', - 'selected' => $module::EDIT_ALL, - 'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'item sans restriction' - ]); ?> -
-
-
-
-
-
-
-
-

Commentaires

-
-
- -
-
- -
-
- 'Choix du nombre maximum de caractères pour chaque commentaire de l\'item, mise en forme html comprise.', - 'label' => 'Caractères par commentaire' - ]); ?> -
-
-
-
- -
-
- -
-
-
-
-
- + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ true, + 'value' => 'Enregistrer en brouillon' + ]); ?> + true + ]); ?> +
+
+ 'Publier' + ]); ?> +
+
+
+
+
+

Informations générales

+
+
+ 'Titre' + ]); ?> +
+
+ 'Version' + ]); ?> +
+
+ 'Date' + ]); ?> +
+
+
+
+ 'Archive du fichier' + ]); ?> +
+
+ 'Capture d\'écran', + 'type' => 1 + ]); ?> +
+
+
+
+
+ 'editorWysiwyg' + ]); ?> +
+
+
+

Options de publication

+
+
+ 'Auteur', + 'selected' => $this->getUser('id'), + 'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false + ]); ?> +
+
+ 'L\'item n\'est visible qu\'après la date de publication prévue.', + 'label' => 'Date de publication', + 'value' => time() + ]); ?> +
+
+ 'Edition / Suppression', + 'selected' => $module::EDIT_ALL, + 'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'item sans restriction' + ]); ?> +
+
+
+
+
+
+
+
+

Commentaires

+
+
+ +
+
+ +
+
+ 'Choix du nombre maximum de caractères pour chaque commentaire de l\'item, mise en forme html comprise.', + 'label' => 'Caractères par commentaire' + ]); ?> +
+
+
+
+ +
+
+ +
+
+
+
+
+ diff --git a/module/download/view/comment/comment.css b/module/download/view/comment/comment.css index 805e9150..dc0024c0 100644 --- a/module/download/view/comment/comment.css +++ b/module/download/view/comment/comment.css @@ -1,18 +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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @author Frédéric Tempez - * @copyright Copyright (C) 2018-2020, Frédéric Tempez - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - - -/** NE PAS EFFACER -* admin.css +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css */ \ No newline at end of file diff --git a/module/download/view/comment/comment.js.php b/module/download/view/comment/comment.js.php index fe2f99d5..958317d7 100644 --- a/module/download/view/comment/comment.js.php +++ b/module/download/view/comment/comment.js.php @@ -1,62 +1,62 @@ -/** - * 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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - - -/** - * Confirmation de suppression - */ -$(".downloadCommentDelete").on("click", function() { - var _this = $(this); - var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; - return core.confirm("Supprimer le commentaire de l'item " + nom + " ?", function() { - $(location).attr("href", _this.attr("href")); - }); -}); - -/** - * Confirmation d'approbation - */ -$(".downloadCommentApproved").on("click", function() { - var _this = $(this); - var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; - return core.confirm("Approuver le commentaire de l'item " + nom + " ?", function() { - $(location).attr("href", _this.attr("href")); - }); -}); - -/** - * Confirmation de rejet - */ -$(".downloadCommentRejected").on("click", function() { - var _this = $(this); - var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; - return core.confirm("Rejeter le commentaire de l'item " + nom + " ?", function() { - $(location).attr("href", _this.attr("href")); - }); -}); - -/** - * Confirmation de suppression en masse - */ -$(".downloadCommentDeleteAll").on("click", function() { - var _this = $(this); - var nombre = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'comment' ])); ?>"; - var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; - if( nombre === "1"){ - var message = "Supprimer le commentaire de l'item " + nom + " ?"; - } else{ - var message = "Supprimer les " + nombre + " commentaires de l'item " + nom + " ?"; - } - return core.confirm(message, function() { - $(location).attr("href", _this.attr("href")); - }); -}); +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** + * Confirmation de suppression + */ +$(".downloadCommentDelete").on("click", function() { + var _this = $(this); + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + return core.confirm("Supprimer le commentaire de l'item " + nom + " ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation d'approbation + */ +$(".downloadCommentApproved").on("click", function() { + var _this = $(this); + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + return core.confirm("Approuver le commentaire de l'item " + nom + " ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation de rejet + */ +$(".downloadCommentRejected").on("click", function() { + var _this = $(this); + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + return core.confirm("Rejeter le commentaire de l'item " + nom + " ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation de suppression en masse + */ +$(".downloadCommentDeleteAll").on("click", function() { + var _this = $(this); + var nombre = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'comment' ])); ?>"; + var nom = "getData(['module', $this->getUrl(0), $this->getUrl(2), 'title' ]); ?>"; + if( nombre === "1"){ + var message = "Supprimer le commentaire de l'item " + nom + " ?"; + } else{ + var message = "Supprimer les " + nombre + " commentaires de l'item " + nom + " ?"; + } + return core.confirm(message, function() { + $(location).attr("href", _this.attr("href")); + }); +}); diff --git a/module/download/view/comment/comment.php b/module/download/view/comment/comment.php index 76c50a61..e42d3fd3 100644 --- a/module/download/view/comment/comment.php +++ b/module/download/view/comment/comment.php @@ -1,22 +1,22 @@ -
-
- 'buttonGrey', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'ico' => 'left', - 'value' => 'Retour' - ]); ?> -
- - -
- -
- -
- - '; ?> - - - - +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+ + +
+ +
+ +
+ + '; ?> + + + + diff --git a/module/download/view/config/config.css b/module/download/view/config/config.css index 805e9150..dc0024c0 100644 --- a/module/download/view/config/config.css +++ b/module/download/view/config/config.css @@ -1,18 +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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @author Frédéric Tempez - * @copyright Copyright (C) 2018-2020, Frédéric Tempez - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - - -/** NE PAS EFFACER -* admin.css +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css */ \ No newline at end of file diff --git a/module/download/view/config/config.js.php b/module/download/view/config/config.js.php index bc462697..d96c2c34 100644 --- a/module/download/view/config/config.js.php +++ b/module/download/view/config/config.js.php @@ -1,21 +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 Rémi Jean - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - -/** - * Confirmation de suppression - */ -$(".downloadConfigDelete").on("click", function() { - var _this = $(this); - return core.confirm("Êtes-vous sûr de vouloir supprimer cet item ?", function() { - $(location).attr("href", _this.attr("href")); - }); +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +/** + * Confirmation de suppression + */ +$(".downloadConfigDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer cet item ?", function() { + $(location).attr("href", _this.attr("href")); + }); }); \ No newline at end of file diff --git a/module/download/view/config/config.php b/module/download/view/config/config.php index 90dfa938..79584139 100644 --- a/module/download/view/config/config.php +++ b/module/download/view/config/config.php @@ -1,52 +1,52 @@ - -
-
- 'buttonGrey', - 'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0), 'items', - 'ico' => 'left', - 'value' => 'Retour' - ]); ?> -
-
- helper::baseUrl() . $this->getUrl(0) . '/add', - 'ico' => 'plus', - 'value' => 'Item' - ]); ?> -
-
- -
-
-
-
-
-

Paramètres du module

-
-
- $this->getData(['module', $this->getUrl(0), 'config', 'feeds']), - ]); ?> -
-
- 'Etiquette du flux', - 'value' => $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel']) - ]); ?> -
-
-
-
-
- - - - - - - -
Version n° - -
- + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0), 'items', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ helper::baseUrl() . $this->getUrl(0) . '/add', + 'ico' => 'plus', + 'value' => 'Item' + ]); ?> +
+
+ +
+
+
+
+
+

Paramètres du module

+
+
+ $this->getData(['module', $this->getUrl(0), 'config', 'feeds']), + ]); ?> +
+
+ 'Etiquette du flux', + 'value' => $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel']) + ]); ?> +
+
+
+
+
+ + + + + + + +
Version n° + +
+ diff --git a/module/download/view/edit/edit.css b/module/download/view/edit/edit.css index 805e9150..dc0024c0 100644 --- a/module/download/view/edit/edit.css +++ b/module/download/view/edit/edit.css @@ -1,18 +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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @author Frédéric Tempez - * @copyright Copyright (C) 2018-2020, Frédéric Tempez - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - - -/** NE PAS EFFACER -* admin.css +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css */ \ No newline at end of file diff --git a/module/download/view/edit/edit.js.php b/module/download/view/edit/edit.js.php index 148e937a..a1300deb 100644 --- a/module/download/view/edit/edit.js.php +++ b/module/download/view/edit/edit.js.php @@ -1,66 +1,66 @@ -/** - * 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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - - -// Lien de connexion -$("#downloadEditMailNotification").on("change", function() { - if($(this).is(":checked")) { - $("#formConfigGroup").show(); - } - else { - $("#formConfigGroup").hide(); - } -}).trigger("change"); - - -/** - * Soumission du formulaire pour enregistrer en brouillon - */ -$("#downloadEditDraft").on("click", function() { - $("#downloadEditState").val(0); - $("#downloadEditForm").trigger("submit"); -}); - -/** - * Options de commentaires - */ -$("#downloadEditCommentClose").on("change", function() { - if ($(this).is(':checked') ) { - $(".commentOptionsWrapper").slideUp(); - } else { - $(".commentOptionsWrapper").slideDown(); - } -}); - -$("#downloadEditCommentNotification").on("change", function() { - if ($(this).is(':checked') ) { - $("#downloadEditCommentGroupNotification").slideDown(); - } else { - $("#downloadEditCommentGroupNotification").slideUp(); - } -}); - - -$( document).ready(function() { - - if ($("#downloadEditCloseComment").is(':checked') ) { - $(".commentOptionsWrapper").slideUp(); - } else { - $(".commentOptionsWrapper").slideDown(); - } - - if ($("#downloadEditCommentNotification").is(':checked') ) { - $("#downloadEditCommentGroupNotification").slideDown(); - } else { - $("#downloadEditCommentGroupNotification").slideUp(); - } +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +// Lien de connexion +$("#downloadEditMailNotification").on("change", function() { + if($(this).is(":checked")) { + $("#formConfigGroup").show(); + } + else { + $("#formConfigGroup").hide(); + } +}).trigger("change"); + + +/** + * Soumission du formulaire pour enregistrer en brouillon + */ +$("#downloadEditDraft").on("click", function() { + $("#downloadEditState").val(0); + $("#downloadEditForm").trigger("submit"); +}); + +/** + * Options de commentaires + */ +$("#downloadEditCommentClose").on("change", function() { + if ($(this).is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } +}); + +$("#downloadEditCommentNotification").on("change", function() { + if ($(this).is(':checked') ) { + $("#downloadEditCommentGroupNotification").slideDown(); + } else { + $("#downloadEditCommentGroupNotification").slideUp(); + } +}); + + +$( document).ready(function() { + + if ($("#downloadEditCloseComment").is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } + + if ($("#downloadEditCommentNotification").is(':checked') ) { + $("#downloadEditCommentGroupNotification").slideDown(); + } else { + $("#downloadEditCommentGroupNotification").slideUp(); + } }); \ No newline at end of file diff --git a/module/download/view/edit/edit.php b/module/download/view/edit/edit.php index 1a395485..7dc32b55 100644 --- a/module/download/view/edit/edit.php +++ b/module/download/view/edit/edit.php @@ -1,145 +1,145 @@ - -
-
- 'buttonGrey', - 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'ico' => 'left', - 'value' => 'Retour' - ]); ?> -
-
- true, - 'value' => 'Enregistrer en brouillon' - ]); ?> - true - ]); ?> -
-
- 'Publier' - ]); ?> - -
-
-
-
-
-

Informations générales

- -
-
- 'Titre', - 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'title']) - ]); ?> -
-
- 'Version', - 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileVersion']) - ]); ?> -
-
- 'Date', - 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileDate']) - ]); ?> -
-
-
-
- 'Archive du fichier', - 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']) - ]); ?> -
-
- 'Capture d\'écran', - 'type' => 1, - 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'picture']) - ]); ?> -
-
-
-
-
- 'editorWysiwyg', - 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'content']) - ]); ?> -
-
-
-

Options de publication

-
-
- 'Auteur', - 'selected' => $this->getUser('id'), - 'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false - ]); ?> -
-
- 'L\'item n\'est visible qu\'après la date de publication prévue.', - 'label' => 'Date de publication', - 'value' => time() - ]); ?> -
-
- 'Edition / Suppression', - 'selected' => is_numeric($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'editConsent'])) ? $module::EDIT_GROUP : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'editConsent']), - 'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'item sans restriction' - ]); ?> -
-
-
-
-
-
-
-
-

Commentaires

-
-
- $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentClose']) - ]); ?> -
-
- $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentApproved']), - '' - ]); ?> -
-
- 'Choix du nombre maximum de caractères pour chaque commentaire de l\'item, mise en forme html comprise.', - 'label' => 'Caractères par commentaire', - 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentMaxlength']) - ]); ?> -
- -
-
-
- $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentNotification']), - ]); ?> -
-
- $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentGroupNotification']), - 'help' => 'Editeurs = éditeurs + administrateurs
Membres = membres + éditeurs + administrateurs' - ]); ?> -
-
-
-
-
- + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ true, + 'value' => 'Enregistrer en brouillon' + ]); ?> + true + ]); ?> +
+
+ 'Publier' + ]); ?> + +
+
+
+
+
+

Informations générales

+ +
+
+ 'Titre', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'title']) + ]); ?> +
+
+ 'Version', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileVersion']) + ]); ?> +
+
+ 'Date', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileDate']) + ]); ?> +
+
+
+
+ 'Archive du fichier', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']) + ]); ?> +
+
+ 'Capture d\'écran', + 'type' => 1, + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'picture']) + ]); ?> +
+
+
+
+
+ 'editorWysiwyg', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'content']) + ]); ?> +
+
+
+

Options de publication

+
+
+ 'Auteur', + 'selected' => $this->getUser('id'), + 'disabled' => $this->getUser('group') !== self::GROUP_ADMIN ? true : false + ]); ?> +
+
+ 'L\'item n\'est visible qu\'après la date de publication prévue.', + 'label' => 'Date de publication', + 'value' => time() + ]); ?> +
+
+ 'Edition / Suppression', + 'selected' => is_numeric($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'editConsent'])) ? $module::EDIT_GROUP : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'editConsent']), + 'help' => 'Les utilisateurs des groupes supérieurs accèdent à l\'item sans restriction' + ]); ?> +
+
+
+
+
+
+
+
+

Commentaires

+
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentClose']) + ]); ?> +
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentApproved']), + '' + ]); ?> +
+
+ 'Choix du nombre maximum de caractères pour chaque commentaire de l\'item, mise en forme html comprise.', + 'label' => 'Caractères par commentaire', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentMaxlength']) + ]); ?> +
+ +
+
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentNotification']), + ]); ?> +
+
+ $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'commentGroupNotification']), + 'help' => 'Editeurs = éditeurs + administrateurs
Membres = membres + éditeurs + administrateurs' + ]); ?> +
+
+
+
+
+ diff --git a/module/download/view/index/index.css b/module/download/view/index/index.css index 6d358cfd..b495b1b9 100644 --- a/module/download/view/index/index.css +++ b/module/download/view/index/index.css @@ -1,68 +1,68 @@ -.rowitem { - margin-bottom: 10px !important; -} -.downloadPicture { - float: none; - border: 1px; -} -.downloadPicture img { - width: 100%; - height: auto; - /* - border:1px solid lightgray; - box-shadow: 1px 1px 5px darkgray; - */ -} - -.downloadPicture:hover { - opacity: .7; -} -.row:after { - content: " "; - display: table; - clear: both; -} -.downloadComment { - padding-right: 10px; - float: right; -} -.downloadTitle { - /*background-color: #ECEFF1;*/ - padding: 0px; - margin-bottom: 5px; -} -.downloadContent { - float: left; - margin-top: 5px; -} -.downloadDate { - font-size:0.8em; - font-style: italic; - /* - color: grey; - */ -} -@media (max-width: 768px) { - .downloadContent { - display: none; - } - - .downloadPicture img { - width: 50% ; - display: block; - margin-left: auto; - margin-right: auto; - } -} - -/* -* Flux RSS -*/ -#rssFeed { - text-align: right; - float: right; -} -#rssFeed p { - display: inline; - vertical-align: top; +.rowitem { + margin-bottom: 10px !important; +} +.downloadPicture { + float: none; + border: 1px; +} +.downloadPicture img { + width: 100%; + height: auto; + /* + border:1px solid lightgray; + box-shadow: 1px 1px 5px darkgray; + */ +} + +.downloadPicture:hover { + opacity: .7; +} +.row:after { + content: " "; + display: table; + clear: both; +} +.downloadComment { + padding-right: 10px; + float: right; +} +.downloadTitle { + /*background-color: #ECEFF1;*/ + padding: 0px; + margin-bottom: 5px; +} +.downloadContent { + float: left; + margin-top: 5px; +} +.downloadDate { + font-size:0.8em; + font-style: italic; + /* + color: grey; + */ +} +@media (max-width: 768px) { + .downloadContent { + display: none; + } + + .downloadPicture img { + width: 50% ; + display: block; + margin-left: auto; + margin-right: auto; + } +} + +/* +* Flux RSS +*/ +#rssFeed { + text-align: right; + float: right; +} +#rssFeed p { + display: inline; + vertical-align: top; } \ No newline at end of file diff --git a/module/download/view/index/index.php b/module/download/view/index/index.php index 387ba0f1..5acb9519 100644 --- a/module/download/view/index/index.php +++ b/module/download/view/index/index.php @@ -1,67 +1,67 @@ - -
-
- $item): ?> -
-
- makeThumb( self::FILE_DIR . 'source/' . $item['picture'], - self::FILE_DIR . 'thumb/' . $thumb, - self::THUMBS_WIDTH); - } - - ?> - - <?php echo $item['picture']; ?> - -
-
- -
-
- -
-
- - getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> - - - - + +
+
+ $item): ?> +
+
+ makeThumb( self::FILE_DIR . 'source/' . $item['picture'], + self::FILE_DIR . 'thumb/' . $thumb, + self::THUMBS_WIDTH); + } + + ?> + + <?php echo $item['picture']; ?> + +
+
+ +
+
+ +
+
+ + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + + + + \ No newline at end of file diff --git a/module/download/view/item/item.css b/module/download/view/item/item.css index 445f719d..f2f806ae 100644 --- a/module/download/view/item/item.css +++ b/module/download/view/item/item.css @@ -1,63 +1,63 @@ - -#sectionTitle { - margin-top: 0; - margin-bottom: 5px; -} -.downloaditemPicture { - height: auto; - border:1px solid lightgray; - box-shadow: 1px 1px 5px; -} -.downloaditemPictureleft { - float: left; - margin: 15px 10px 5px 0 ; -} -.downloaditemPictureright { - 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 ; -} - -#downloaditemCommentShow { - cursor: text; -} -#downloaditemOr { - padding: 10px; -} -.downloadDate { - font-size:0.8em; - font-style: italic; - color: grey; -} -@media (max-width: 767px) { - .downloaditemPicture { - height:auto; - max-width: 100%;} - } - - -#rssFeed { - text-align: right; - float: right; -} -#rssFeed p { - display: inline; - vertical-align: top; -} + +#sectionTitle { + margin-top: 0; + margin-bottom: 5px; +} +.downloaditemPicture { + height: auto; + border:1px solid lightgray; + box-shadow: 1px 1px 5px; +} +.downloaditemPictureleft { + float: left; + margin: 15px 10px 5px 0 ; +} +.downloaditemPictureright { + 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 ; +} + +#downloaditemCommentShow { + cursor: text; +} +#downloaditemOr { + padding: 10px; +} +.downloadDate { + font-size:0.8em; + font-style: italic; + color: grey; +} +@media (max-width: 767px) { + .downloaditemPicture { + height:auto; + max-width: 100%;} + } + + +#rssFeed { + text-align: right; + float: right; +} +#rssFeed p { + display: inline; + vertical-align: top; +} diff --git a/module/download/view/item/item.js.php b/module/download/view/item/item.js.php index 1b1ace98..2748a7c8 100644 --- a/module/download/view/item/item.js.php +++ b/module/download/view/item/item.js.php @@ -1,43 +1,43 @@ -/** - * 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 - * @copyright Copyright (C) 2008-2018, Rémi Jean - * @license GNU General Public License, version 3 - * @link http://zwiicms.fr/ - */ - -/** - * Affiche le bloc pour rédiger un commentaire - */ -var commentShowDOM = $("#downloaditemCommentShow"); -commentShowDOM.on("click focus", function() { - $("#downloaditemCommentShowWrapper").fadeOut(function() { - $("#downloaditemCommentWrapper").fadeIn(); - $("#downloaditemCommentContent").trigger("focus"); - }); -}); -if($("#downloaditemCommentWrapper").find("textarea.notice,input.notice").length) { - commentShowDOM.trigger("click"); -} - -/** - * Cache le bloc pour rédiger un commentaire - */ -$("#downloaditemCommentHide").on("click focus", function() { - $("#downloaditemCommentWrapper").fadeOut(function() { - $("#downloaditemCommentShowWrapper").fadeIn(); - $("#downloaditemCommentContent").val(""); - $("#downloaditemCommentAuthor").val(""); - }); -}); - -/** - * Force le scroll vers les commentaires en cas d'erreur - */ -$("#downloaditemCommentForm").on("submit", function() { - $(location).attr("href", "#comment"); +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + +/** + * Affiche le bloc pour rédiger un commentaire + */ +var commentShowDOM = $("#downloaditemCommentShow"); +commentShowDOM.on("click focus", function() { + $("#downloaditemCommentShowWrapper").fadeOut(function() { + $("#downloaditemCommentWrapper").fadeIn(); + $("#downloaditemCommentContent").trigger("focus"); + }); +}); +if($("#downloaditemCommentWrapper").find("textarea.notice,input.notice").length) { + commentShowDOM.trigger("click"); +} + +/** + * Cache le bloc pour rédiger un commentaire + */ +$("#downloaditemCommentHide").on("click focus", function() { + $("#downloaditemCommentWrapper").fadeOut(function() { + $("#downloaditemCommentShowWrapper").fadeIn(); + $("#downloaditemCommentContent").val(""); + $("#downloaditemCommentAuthor").val(""); + }); +}); + +/** + * Force le scroll vers les commentaires en cas d'erreur + */ +$("#downloaditemCommentForm").on("submit", function() { + $(location).attr("href", "#comment"); }); \ No newline at end of file diff --git a/module/download/view/item/item.php b/module/download/view/item/item.php index 477e4a67..7e468a3e 100644 --- a/module/download/view/item/item.php +++ b/module/download/view/item/item.php @@ -1,188 +1,188 @@ -
-
-
-
- - getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) - ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) - : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); - $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) - ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) - : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); - echo $date . ' à ' . $heure; - ?> -
-
-
- getUser('password') === $this->getInput('ZWII_USER_PASSWORD') - AND - ( // Propriétaire - ( - $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_OWNER - AND ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'userId']) === $this->getUser('id') - OR $this->getUser('group') === self::GROUP_ADMIN ) - ) - OR ( - // Groupe - ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === self::GROUP_ADMIN - OR $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === self::GROUP_MODERATOR) - AND $this->getUser('group') >= $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) - ) - OR ( - // Tout le monde - $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_ALL - AND $this->getUser('group') >= $module::$actions['config'] - ) - ) - ): ?> - helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], - 'value' => 'Editer' - ]); ?> - -
-
-
-
-
-
- getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']); ?> - getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . - '" alt="' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . '">'; - ?> -
-
-
-
- self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file']), - 'href' => helper::baseUrl() . $this->getUrl(0) . '/downloadFile/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], - 'value' => 'Télécharger' - ]); ?> -
-
-
-
- getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileVersion']); ?> -
-
-
-
- getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])), 'UTF-8', true) - ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])) - : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate']))); - ?> - -
-
-
-
- getData(['module', $this->getUrl(0),'items', $this->getUrl(1), 'content']); ?> -
-
-

-
-
- - getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> - - - getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentClose'])): ?> -

Cet item ne reçoit pas de commentaire.

- -

- - - - 0 ? $commentsNb . ' ' . 'commentaire' . $s : 'Pas encore de commentaire'; ?> -

- - 'Rédiger un commentaire...', - 'readonly' => true - ]); ?> -
- getUser('password') === $this->getInput('ZWII_USER_PASSWORD')): ?> - 'Nom', - 'readonly' => true, - 'value' => $module::$editCommentSignature - ]); ?> - $this->getUser('id') - ]); ?> - -
-
- 'Nom' - ]); ?> -
-
-
Ou
-
-
- helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl()) . '__comment', - 'value' => 'Connexion' - ]); ?> -
-
- - 'Commentaire avec maximum '.$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']).' caractères', - 'class' => 'editorWysiwygComment', - 'noDirty' => true, - 'maxlength' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']) - ]); ?> -
- getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')): ?> -
-
- $this->getData(['config','captchaStrong']) - ]); ?> -
-
- -
-
- 'buttonGrey', - 'value' => 'Annuler' - ]); ?> -
-
- 'Envoyer', - 'ico' => '' - ]); ?> -
-
-
- -
-
- $comment): ?> -
-

- le - -

- -
-
- +
+
+
+
+ + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); + $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) + ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) + : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); + echo $date . ' à ' . $heure; + ?> +
+
+
+ getUser('password') === $this->getInput('ZWII_USER_PASSWORD') + AND + ( // Propriétaire + ( + $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_OWNER + AND ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'userId']) === $this->getUser('id') + OR $this->getUser('group') === self::GROUP_ADMIN ) + ) + OR ( + // Groupe + ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === self::GROUP_ADMIN + OR $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === self::GROUP_MODERATOR) + AND $this->getUser('group') >= $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) + ) + OR ( + // Tout le monde + $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_ALL + AND $this->getUser('group') >= $module::$actions['config'] + ) + ) + ): ?> + helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], + 'value' => 'Editer' + ]); ?> + +
+
+
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']); ?> + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . + '" alt="' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . '">'; + ?> +
+
+
+
+ self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file']), + 'href' => helper::baseUrl() . $this->getUrl(0) . '/downloadFile/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], + 'value' => 'Télécharger' + ]); ?> +
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileVersion']); ?> +
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate']))); + ?> + +
+
+
+
+ getData(['module', $this->getUrl(0),'items', $this->getUrl(1), 'content']); ?> +
+
+

+
+
+ + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + + + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentClose'])): ?> +

Cet item ne reçoit pas de commentaire.

+ +

+ + + + 0 ? $commentsNb . ' ' . 'commentaire' . $s : 'Pas encore de commentaire'; ?> +

+ + 'Rédiger un commentaire...', + 'readonly' => true + ]); ?> +
+ getUser('password') === $this->getInput('ZWII_USER_PASSWORD')): ?> + 'Nom', + 'readonly' => true, + 'value' => $module::$editCommentSignature + ]); ?> + $this->getUser('id') + ]); ?> + +
+
+ 'Nom' + ]); ?> +
+
+
Ou
+
+
+ helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl()) . '__comment', + 'value' => 'Connexion' + ]); ?> +
+
+ + 'Commentaire avec maximum '.$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']).' caractères', + 'class' => 'editorWysiwygComment', + 'noDirty' => true, + 'maxlength' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']) + ]); ?> +
+ getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')): ?> +
+
+ $this->getData(['config','captchaStrong']) + ]); ?> +
+
+ +
+
+ 'buttonGrey', + 'value' => 'Annuler' + ]); ?> +
+
+ 'Envoyer', + 'ico' => '' + ]); ?> +
+
+
+ +
+
+ $comment): ?> +
+

+ le + +

+ +
+
+
\ No newline at end of file diff --git a/module/download/view/stats/stats.css b/module/download/view/stats/stats.css new file mode 100644 index 00000000..dc0024c0 --- /dev/null +++ b/module/download/view/stats/stats.css @@ -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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/module/download/view/stats/stats.php b/module/download/view/stats/stats.php new file mode 100644 index 00000000..e69de29b From af94b2e5bc32a118837c7ce8e2d234f6ae3243bf Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Fri, 12 Feb 2021 17:06:38 +0100 Subject: [PATCH 05/18] Stats en cours --- module/download/download.php | 66 ++++++++++++++++++++++++++-- module/download/view/stats/stats.css | 18 ++++++++ module/download/view/stats/stats.php | 31 +++++++++++++ 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 module/download/view/stats/stats.css create mode 100644 module/download/view/stats/stats.php diff --git a/module/download/download.php b/module/download/download.php index f99bbd9a..17fcead6 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -27,6 +27,7 @@ class download extends common { 'config' => self::GROUP_MODERATOR, 'delete' => self::GROUP_MODERATOR, 'edit' => self::GROUP_MODERATOR, + 'stats' => self::GROUP_MODERATOR, 'index' => self::GROUP_VISITOR, 'rss' => self::GROUP_VISITOR, 'downloadFile' => self::GROUP_VISITOR @@ -426,7 +427,6 @@ class download extends common { } // Nombre de téléchargements $stats = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'fileStats']), 'time'); - // Met en forme le tableau $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) @@ -440,7 +440,7 @@ class download extends common { '', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileVersion']), $date .' à '. $heure, - count($stats), + '' . count($stats) . '', self::$states[$this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'state'])], // Bouton pour afficher les commentaires de l'item template::button('downloadConfigComment' . $itemIds[$i], [ @@ -771,6 +771,7 @@ class download extends common { * Initie un téléchargement protégé */ public function downloadFile() { + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { // Valeurs en sortie $this->addOutput([ @@ -825,5 +826,64 @@ class download extends common { } } } -} + /** + * Ecran de consultation des données statistiques + */ + + public function stats() { + // Soumission du formulaire + if($this->isPost()) { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'notification' => 'Action non autorisée' + ]); + } + // L'item n'existe pas + if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + } else { + // Construction de la page des statistiques + // Ids des items par ordre de publication + + $itemIds = array_keys($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); + + // Liste des pages + self::$pages = $pagination['pages']; + + for($i = $pagination['first']; $i < $pagination['last']; $i++) { + + // Format des variables + + echo $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate']); + + $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate'])), 'UTF-8', true) + ? strftime('%d %B %Y',$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate']))); + + // Met en forme le tableau + self::$items[] = [ + $date, + $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileStats', 'ip']) + ]; + + } + die(); + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Statistiques de téléchargement', + 'view' => 'stats' + ]); + + } + } +} \ No newline at end of file diff --git a/module/download/view/stats/stats.css b/module/download/view/stats/stats.css new file mode 100644 index 00000000..805e9150 --- /dev/null +++ b/module/download/view/stats/stats.css @@ -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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** NE PAS EFFACER +* admin.css +*/ \ No newline at end of file diff --git a/module/download/view/stats/stats.php b/module/download/view/stats/stats.php new file mode 100644 index 00000000..9bcc3316 --- /dev/null +++ b/module/download/view/stats/stats.php @@ -0,0 +1,31 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0), 'items', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ helper::baseUrl() . $this->getUrl(0) . '/add', + 'ico' => 'plus', + 'value' => 'Item' + ]); ?> +
+
+ +
+
+ + + + + + + +
Version n° + +
\ No newline at end of file From 4ba7c59d32a19a6c5c3b66f2367534e21cbfb45a Mon Sep 17 00:00:00 2001 From: fredtempez Date: Fri, 12 Feb 2021 18:37:31 +0100 Subject: [PATCH 06/18] Stat ok manque le total dans la liste et l'export --- module/download/download.php | 91 +++++++++++++------------ module/download/view/stats/stats.js.php | 22 ++++++ module/download/view/stats/stats.php | 14 ++-- 3 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 module/download/view/stats/stats.js.php diff --git a/module/download/download.php b/module/download/download.php index 17fcead6..830f77c2 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -28,6 +28,7 @@ class download extends common { 'delete' => self::GROUP_MODERATOR, 'edit' => self::GROUP_MODERATOR, 'stats' => self::GROUP_MODERATOR, + 'statsDeleteAll' => self::GROUP_MODERATOR, 'index' => self::GROUP_VISITOR, 'rss' => self::GROUP_VISITOR, 'downloadFile' => self::GROUP_VISITOR @@ -795,7 +796,7 @@ class download extends common { $statId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); $this->setData(['module', $this->getUrl(0), - 'items', + 'items', $this->getUrl(2), 'fileStats', $statId, [ @@ -832,58 +833,60 @@ class download extends common { */ public function stats() { - // Soumission du formulaire - if($this->isPost()) { - // Jeton incorrect - if ($this->getUrl(3) !== $_SESSION['csrf']) { - // Valeurs en sortie - $this->addOutput([ - 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/config', - 'notification' => 'Action non autorisée' - ]); - } - // L'item n'existe pas - if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2)]) === null) { - // Valeurs en sortie - $this->addOutput([ - 'access' => false - ]); - } - } else { + // Construction de la page des statistiques - // Ids des items par ordre de publication + $itemIds = array_keys($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); - $itemIds = array_keys($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); - // Pagination - $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pages = $pagination['pages']; - // Liste des pages - self::$pages = $pagination['pages']; + for($i = $pagination['first']; $i < $pagination['last']; $i++) { - for($i = $pagination['first']; $i < $pagination['last']; $i++) { + // Format des variables + $date = mb_detect_encoding(strftime('%d %B %Y - %H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'time'])), 'UTF-8', true) + ? strftime('%d %B %Y - %H:%M',$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'time'])) + : utf8_encode(strftime('%d %B %Y - %H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'time']))); - // Format des variables - - echo $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate']); - - $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate'])), 'UTF-8', true) - ? strftime('%d %B %Y',$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate'])) - : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'fileDate']))); + // Met en forme le tableau + self::$items[] = [ + $date, + $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', $itemIds[$i], 'ip']) + ]; - // Met en forme le tableau - self::$items[] = [ - $date, - $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileStats', 'ip']) - ]; - - } - die(); + } + $this->addOutput([ + 'title' => 'Statistiques de téléchargement', + 'view' => 'stats' + ]); + } + + public function statsDeleteAll() { + // Validité de la page demandée + if($this->getData(['module', $this->getUrl(0), 'items']) === null) { // Valeurs en sortie $this->addOutput([ - 'title' => 'Statistiques de téléchargement', - 'view' => 'stats' + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0), + 'notification' => 'Action non autorisée' + ]); + } + // Téléchargement + else { + $this->setData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats', [] ]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/stats/' . $this->getUrl(2), + 'notification' => 'Purge des statistiques', + 'state' => true ]); - } } } \ No newline at end of file diff --git a/module/download/view/stats/stats.js.php b/module/download/view/stats/stats.js.php new file mode 100644 index 00000000..fbeffea1 --- /dev/null +++ b/module/download/view/stats/stats.js.php @@ -0,0 +1,22 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.fr/ + */ + + +/** + * Confirmation de suppression + */ +$(".statsDeleteAll").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir purger les statistiques ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); \ No newline at end of file diff --git a/module/download/view/stats/stats.php b/module/download/view/stats/stats.php index 9bcc3316..072bdf8b 100644 --- a/module/download/view/stats/stats.php +++ b/module/download/view/stats/stats.php @@ -3,21 +3,19 @@
'buttonGrey', - 'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0), 'items', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', 'ico' => 'left', 'value' => 'Retour' ]); ?>
-
+
helper::baseUrl() . $this->getUrl(0) . '/add', - 'ico' => 'plus', - 'value' => 'Item' + 'class' => 'statsDeleteAll buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/statsDeleteAll' . '/' . $this->getUrl(2) . '/'. $_SESSION['csrf'] , + 'ico' => 'cancel', + 'value' => 'Tout effacer' ]); ?>
-
- -
From 6a7642e5a06bf9fc1ee0016896c8792d14b16a90 Mon Sep 17 00:00:00 2001 From: fredtempez Date: Sun, 14 Feb 2021 14:21:58 +0100 Subject: [PATCH 07/18] =?UTF-8?q?Module=20t=C3=A9l=C3=A9chargement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/download/download.php | 26 +++++++++++++++++++------- module/download/view/config/config.php | 2 +- module/download/view/index/index.css | 7 ++++--- module/download/view/index/index.php | 8 ++++---- module/download/view/item/item.php | 7 +++++++ module/download/view/stats/stats.php | 9 ++++++++- 6 files changed, 43 insertions(+), 16 deletions(-) diff --git a/module/download/download.php b/module/download/download.php index 830f77c2..69cb3755 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -53,6 +53,9 @@ class download extends common { public static $pages; + // Nombre de téléchargements + public static $statSum = 0; + public static $states = [ false => 'Brouillon', true => 'Publié' @@ -90,7 +93,7 @@ class download extends common { public static $users = []; - const VERSION = '4.2'; + const VERSION = '1.0'; const REALNAME = 'Téléchargement'; /** @@ -426,22 +429,25 @@ class download extends common { $toApprove = 0; $approved = count($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'comment'])); } - // Nombre de téléchargements - $stats = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'fileStats']), 'time'); // Met en forme le tableau $date = mb_detect_encoding(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); - $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) - ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) - : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); + $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])), 'UTF-8', true) + ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate'])) + : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileDate']))); + $stat = count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'fileStats']), 'time') ) === 0 + ? '0' + : '' . + count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'fileStats']), 'time') ) . + ''; self::$items[] = [ '' . $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'title']) . '', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'fileVersion']), $date .' à '. $heure, - '' . count($stats) . '', + $stat, self::$states[$this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'state'])], // Bouton pour afficher les commentaires de l'item template::button('downloadConfigComment' . $itemIds[$i], [ @@ -684,6 +690,10 @@ class download extends common { $commentIds = array_keys(helper::arrayCollumn($commentsApproved, 'createdOn', 'SORT_DESC')); // Pagination $pagination = helper::pagination($commentIds, $this->getUrl(),$this->getData(['config','itemsperPage']),'#comment'); + // Nombre de téléchargements + self::$statSum = count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items',$this->getUrl(1),'fileStats']), 'time') ) === 0 + ? '0' + : count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'fileStats']), 'time') ) ; // Liste des pages self::$pages = $pagination['pages']; // Signature de l'item @@ -836,6 +846,8 @@ class download extends common { // Construction de la page des statistiques $itemIds = array_keys($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileStats'])); + // Total des téléchargements + self::$statSum = count ($itemIds); // Pagination $pagination = helper::pagination($itemIds, $this->getUrl(),$this->getData(['config','itemsperPage'])); diff --git a/module/download/view/config/config.php b/module/download/view/config/config.php index 79584139..78de2ebc 100644 --- a/module/download/view/config/config.php +++ b/module/download/view/config/config.php @@ -12,7 +12,7 @@ helper::baseUrl() . $this->getUrl(0) . '/add', 'ico' => 'plus', - 'value' => 'Item' + 'value' => 'Fichier' ]); ?>
diff --git a/module/download/view/index/index.css b/module/download/view/index/index.css index b495b1b9..21e784f9 100644 --- a/module/download/view/index/index.css +++ b/module/download/view/index/index.css @@ -26,10 +26,11 @@ padding-right: 10px; float: right; } -.downloadTitle { - /*background-color: #ECEFF1;*/ - padding: 0px; +h2{ margin-bottom: 5px; + margin-top: 0px; + padding: 0px; + } .downloadContent { float: left; diff --git a/module/download/view/index/index.php b/module/download/view/index/index.php index 5acb9519..75a06084 100644 --- a/module/download/view/index/index.php +++ b/module/download/view/index/index.php @@ -3,7 +3,7 @@
$item): ?>
- +
+
+

Nombre de téléchargements : + +

+
+
From 79442c91746ad6473699488c664f85709b7413de Mon Sep 17 00:00:00 2001 From: fredtempez Date: Sat, 27 Feb 2021 18:26:57 +0100 Subject: [PATCH 08/18] =?UTF-8?q?Conformit=C3=A9=20avec=20les=20nouvelles?= =?UTF-8?q?=20nouvelles=20constantes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/download/download.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/module/download/download.php b/module/download/download.php index 69cb3755..f7c97f2a 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -14,6 +14,12 @@ class download extends common { + const VERSION = '1.0'; + const REALNAME = 'Téléchargement'; + const DELETE = true; + const UPDATE = true; + 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'; @@ -93,10 +99,7 @@ class download extends common { public static $users = []; - const VERSION = '1.0'; - const REALNAME = 'Téléchargement'; - - /** + /** * Flux RSS */ public function rss() { From b76724107ef3cdf00788d25af65c3bf81ce5888c Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Wed, 3 Mar 2021 14:22:58 +0100 Subject: [PATCH 09/18] ajout des autres modules --- module/registration/registration.php | 388 ++++++++++++++++ module/registration/view/config/config.css | 15 + module/registration/view/config/config.php | 90 ++++ module/registration/view/edit/edit.css | 16 + module/registration/view/edit/edit.js.php | 19 + module/registration/view/edit/edit.php | 109 +++++ module/registration/view/index/index.js.php | 48 ++ module/registration/view/index/index.php | 81 ++++ module/registration/view/user/user.css | 16 + module/registration/view/user/user.js.php | 21 + module/registration/view/user/user.php | 15 + .../registration/view/validate/validate.php | 2 + module/sondage/sondage.php | 419 ++++++++++++++++++ .../vendor/html-sortable/html-sortable.min.js | 1 + module/sondage/vendor/html-sortable/inc.json | 3 + module/sondage/view/config/config.css | 3 + module/sondage/view/config/config.js.php | 163 +++++++ module/sondage/view/config/config.php | 155 +++++++ module/sondage/view/data/data.js.php | 31 ++ module/sondage/view/data/data.php | 28 ++ module/sondage/view/index/index.js.php | 24 + module/sondage/view/index/index.php | 66 +++ module/sondage/view/result/result.php | 12 + module/version/version.php | 30 ++ module/version/view/index/index.php | 1 + 25 files changed, 1756 insertions(+) create mode 100644 module/registration/registration.php create mode 100644 module/registration/view/config/config.css create mode 100644 module/registration/view/config/config.php create mode 100644 module/registration/view/edit/edit.css create mode 100644 module/registration/view/edit/edit.js.php create mode 100644 module/registration/view/edit/edit.php create mode 100644 module/registration/view/index/index.js.php create mode 100644 module/registration/view/index/index.php create mode 100644 module/registration/view/user/user.css create mode 100644 module/registration/view/user/user.js.php create mode 100644 module/registration/view/user/user.php create mode 100644 module/registration/view/validate/validate.php create mode 100644 module/sondage/sondage.php create mode 100644 module/sondage/vendor/html-sortable/html-sortable.min.js create mode 100644 module/sondage/vendor/html-sortable/inc.json create mode 100644 module/sondage/view/config/config.css create mode 100644 module/sondage/view/config/config.js.php create mode 100644 module/sondage/view/config/config.php create mode 100644 module/sondage/view/data/data.js.php create mode 100644 module/sondage/view/data/data.php create mode 100644 module/sondage/view/index/index.js.php create mode 100644 module/sondage/view/index/index.php create mode 100644 module/sondage/view/result/result.php create mode 100644 module/version/version.php create mode 100644 module/version/view/index/index.php diff --git a/module/registration/registration.php b/module/registration/registration.php new file mode 100644 index 00000000..c0a35d83 --- /dev/null +++ b/module/registration/registration.php @@ -0,0 +1,388 @@ + + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + +class registration extends common { + + const VERSION = '1.0'; + const REALNAME = 'Enregistrement'; + const DELETE = true; + const UPDATE = true; + const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) + + const STATUS_AWAITING = NULL; // En attente de validation du mail + const STATUS_VALIDATED = -2; // Mail validé en attente d'un admin + + public static $actions = [ + 'index' => self::GROUP_VISITOR, + 'validate' => self::GROUP_VISITOR, + 'config' => self::GROUP_ADMIN, + 'user' => self::GROUP_ADMIN, + 'delete' => self::GROUP_ADMIN, + 'edit' => self::GROUP_ADMIN + ]; + + public static $statusGroups = [ + self::STATUS_AWAITING => 'En attente', + self::STATUS_VALIDATED => 'Email validé', + ]; + + public static $timeLimit = [ + 2 => '2 minutes', + 5 => '5 minutes', + 10 => '10 minutes' + ]; + + public static $users = []; + + + + /** + * Liste des utilisateurs en attente + */ + public function user() { + $userIdsFirstnames = helper::arrayCollumn($this->getData(['user']), 'firstname'); + ksort($userIdsFirstnames); + foreach($userIdsFirstnames as $userId => $userFirstname) { + if ( $this->getData(['user',$userId,'group']) === self::STATUS_AWAITING || + $this->getData(['user',$userId,'group']) === self::STATUS_VALIDATED ) { + self::$users[] = [ + $userId, + $userFirstname . ' ' . $this->getData(['user', $userId, 'lastname']), + self::$statusGroups[$this->getData(['user', $userId, 'group'])] , + utf8_encode( date('Y-m-d G:i', $this->getData(['user', $userId, 'timer']))), + template::button('registrationUserEdit' . $userId, [ + 'href' => helper::baseUrl() . $this->getUrl(0) . '/edit/' . $userId . '/' . $_SESSION['csrf'], + 'value' => template::ico('pencil') + ]), + template::button('registrationUserDelete' . $userId, [ + 'class' => 'userDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $userId . '/' . $_SESSION['csrf'], + 'value' => template::ico('cancel') + ]) + ]; + } + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Demandes d\'inscription', + 'view' => 'user' + ]); + } + + + /** + * Édition + */ + public function edit() { + if ($this->getUrl(3) !== $_SESSION['csrf'] && + $this->getUrl(4) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . 'user', + 'notification' => 'Action non autorisée' + ]); + } + // Accès refusé + if( + // L'utilisateur n'existe pas + $this->getData(['user', $this->getUrl(2)]) === null + // Droit d'édition + AND ( + // Impossible de s'auto-éditer + ( + $this->getUser('id') === $this->getUrl(2) + AND $this->getUrl('group') <= self::GROUP_VISITOR + ) + // Impossible d'éditer un autre utilisateur + OR ($this->getUrl('group') < self::GROUP_MODERATOR) + ) + ) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Accès autorisé + else { + // Soumission du formulaire + if($this->isPost()) { + // Modification du groupe + $this->setData([ + 'user', + $this->getUrl(2), + [ + 'firstname' => $this->getData(['user',$this->getUrl(2),'firstname']), + 'forgot' => 0, + 'group' => $this->getInput('registrationUserEditGroup',helper::FILTER_INT), + 'lastname' => $this->getData(['user',$this->getUrl(2),'lastname']), + 'mail' => $this->getData(['user',$this->getUrl(2),'mail']), + 'password' => $this->getData(['user',$this->getUrl(2),'password']), + 'connectFail' => $this->getData(['user',$this->getUrl(2),'connectFail']), + 'connectTimeout' => $this->getData(['user',$this->getUrl(2),'connectTimeout']), + 'accessUrl' => $this->getData(['user',$this->getUrl(2),'accessUrl']), + 'accessTimer' => $this->getData(['user',$this->getUrl(2),'accessTimer']), + 'accessCsrf' => $this->getData(['user',$this->getUrl(2),'accessCsrf']) + ] + ]); + // Notifier le user uniquement si le groupe est membre au moins membre + if ($this->getInput('registrationUserEditGroup') >= 1 ) { + $this->sendMail( + $this->getData(['user',$this->getUrl(2),'mail']), + 'Approbation de l\'inscription', + '

' . $this->getdata(['module','registration',$this->getUrl(0),'config','mailValidateContent']) . '

' + + ); + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/user', + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => $this->getData(['user', $this->getUrl(2), 'firstname']) . ' ' . $this->getData(['user', $this->getUrl(2), 'lastname']), + 'view' => 'edit' + ]); + } + } + + + /** + * Suppression + */ + public function delete() { + // Accès refusé + if( + // L'utilisateur n'existe pas + $this->getData(['user', $this->getUrl(2)]) === null + // Groupe insuffisant + AND ($this->getUrl('group') < self::GROUP_MODERATOR) + ) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Jeton incorrect + elseif ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/user', + 'notification' => 'Action non autorisée' + ]); + } + // Bloque la suppression de son propre compte + elseif($this->getUser('id') === $this->getUrl(2)) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/user', + 'notification' => 'Impossible de supprimer votre propre compte' + ]); + } + // Suppression + else { + $this->deleteData(['user', $this->getUrl(2)]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/user', + 'notification' => 'Utilisateur supprimé', + 'state' => true + ]); + } + } + + + /** + * Ajout + */ + public function index() { + // Soumission du formulaire + if($this->isPost()) { + $check=true; + // L'identifiant d'utilisateur est indisponible + $userId = $this->getInput('registrationAddId', helper::FILTER_ID, true); + if($this->getData(['module','registration', $userId])) { + self::$inputNotices['registrationAddId'] = 'Identifiant déjà utilisé'; + $check=false; + } + // Double vérification pour le mot de passe + if($this->getInput('registrationAddPassword', helper::FILTER_STRING_SHORT, true) !== $this->getInput('registrationAddConfirmPassword', helper::FILTER_STRING_SHORT, true)) { + self::$inputNotices['registrationAddConfirmPassword'] = 'Incorrect'; + $check = false; + } + // Le mail existe déjà + foreach($this->getData(['user']) as $usersId => $user) { + if($user['mail'] === $this->getInput('registrationAddMail', helper::FILTER_MAIL, true) ) { + self::$inputNotices['registrationAddMail'] = 'Mail déjà utilisé'; + $check = false; + break; + } + } + // Données de l'utilisateur + $userFirstname = $this->getInput('registrationAddFirstname', helper::FILTER_STRING_SHORT, true); + $userLastname = $this->getInput('registrationAddLastname', helper::FILTER_STRING_SHORT, true); + $userMail = $this->getInput('registrationAddMail', helper::FILTER_MAIL, true); + $userTimer = $this->getInput('registrationAddTimer', helper::FILTER_INT, true); + // Pas de nom saisi + if (empty($userFirstname) || + empty($userLastname) || + empty($this->getInput('registrationAddPassword', helper::FILTER_STRING_SHORT, true)) || + empty($this->getInput('registrationAddConfirmPassword', helper::FILTER_STRING_SHORT, true))) { + $check=false; + } + // Si tout est ok + if ($check === true) { + // création effective temporaire + $this->setData([ + 'user', + $userId, + [ + 'firstname' => $userFirstname, + 'lastname' => $userLastname, + 'mail' => $userMail, + 'password' => $this->getInput('registrationAddPassword', helper::FILTER_PASSWORD, true), + // pas de groupe afin de le différencier dans la liste des users + 'group' => null, + 'forgot' => 0, + 'timer' => $userTimer, + 'auth' => $_SESSION['csrf'], + 'status' => self::STATUS_AWAITING + ] + ]); + // Mail d'avertissement aux administrateurs + // Utilisateurs dans le groupe admin + $to = []; + foreach($this->getData(['user']) as $userId => $user) { + if($user['group'] == self::GROUP_ADMIN) { + $to[] = $user['mail']; + } + } + // Envoi du mail + if($to) { + $messageAdmin = $this->getdata(['module','registration',$this->getUrl(0),'config','state']) ? 'Une demande d\'inscription attend l`approbation d\'un administrateur.' : 'Un nouveau membre s\'est inscrit.'; + // Envoi le mail + $this->sendMail( + $to, + 'Auto-inscription sur le site ' . $this->getData(['config', 'title']), + '

' . $messageAdmin . '

' . + '

Identifiant du compte : ' . $userId .' (' . $userFirstname . ' ' . $userLastname . ')
' . + 'Email : ' . $userMail . '

' . + '
Validation de l\'inscription' + ); + } + + // Mail de confirmation à l'utilisateur + // forger le lien de vérification + $validateLink = helper::baseUrl(true) . $this->getUrl() . '/validate/' . $userId . '/' . $_SESSION['csrf']; + // Envoi + $sentMailtoUser = false; + if($check === true) { + $sentMailtoUser = $this->sendMail( + $userMail, + 'Confirmation de votre inscription', + '

' . $this->getdata(['module','registration',$this->getUrl(0),'config','mailRegisterContent']) . '

' . + 'Activer votre compte' + ); + } + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl(), + //'redirect' => $validateLink, + 'notification' => $sentMailtoUser ? "Consultez votre messagerie, un mail vous a été envoyé." : 'Quelque chose n\'a pas fonctionné !', + 'state' => $sentMailtoUser ? true : false + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Inscription', + 'view' => 'index', + 'showBarEditButton' => true, + 'showPageContent' => true + ]); + } + + /** + * Vérification de l'email + */ + public function validate() { + // Vérifie la session + l'id + le timer + $check = true; + $notification = 'Bienvenue sur le site' . $this->getData(['config', 'title']) ; + $csrf = $this->getUrl(3); + $userId = $this->getUrl(2); + // Validité + if ( time() - $this->getData(['user',$userId,'timer']) <= (60 * $this->getdata(['module','registration',$this->getUrl(0),'config','pageTimeOut'])) ) { + $check = false; + $notification = 'Le lien n\'est plus valide'; + } + if (( $csrf !== $this->getData(['user',$userId,'auth']) ) ) { + $check = false; + $notification = 'Identifiant ou mot de passe inconnu'; + } + if ($check) { + $this->setData([ + 'user', + $userId, + [ + 'firstname' => $this->getData(['user',$userId,'firstname']), + 'lastname' => $this->getData(['user',$userId,'lastname']), + 'mail' => $this->getData(['user',$userId,'mail']), + 'password' => $this->getData(['user',$userId,'password']), + 'group' => $this->getdata(['module','registration',$this->getUrl(0),'config','state']) === true ? self::STATUS_VALIDATED : self::GROUP_MEMBER, + 'forgot' => 0, + 'timer' => $this->getData(['user',$userId,'timer']) + ] + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => $check ? helper::baseUrl() . $this->getdata(['module','registration',$this->getUrl(0),'config','pageSuccess']) : helper::baseUrl() . $this->getdata(['module','registration',$this->getUrl(0),'config','pageError']) , + 'notificaton' => $notification, + 'state' => $check + ]); + } + + /** + * Module de configuration + */ + public function config() { + // Soumission du formulaire + if($this->isPost()) { + // Lire les options et les enregistrer + $this->setData(['module','registration',$this->getUrl(0),'config', [ + 'timeOut' => $this->getInput('registrationConfigTimeOut',helper::FILTER_INT), + 'pageSuccess' => $this->getInput('registrationConfigSuccess'), + 'pageError' => $this->getInput('registrationConfigError'), + 'state' => $this->getInput('registrationConfigState',helper::FILTER_BOOLEAN), + 'mailRegisterContent' => $this->getInput('registrationconfigMailRegisterContent', null, true), + 'mailValidateContent' => $this->getInput('registrationconfigMailValidateContent', null, true), + ]]); + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(), + 'notification' => 'Modifications enregistrées', + 'state' => true + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Configuration', + 'view' => 'config', + 'vendor' => ['tinymce'] + ]); + } +} + diff --git a/module/registration/view/config/config.css b/module/registration/view/config/config.css new file mode 100644 index 00000000..9774521b --- /dev/null +++ b/module/registration/view/config/config.css @@ -0,0 +1,15 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + +@import url("core/layout/admin.css"); \ No newline at end of file diff --git a/module/registration/view/config/config.php b/module/registration/view/config/config.php new file mode 100644 index 00000000..bdea4aed --- /dev/null +++ b/module/registration/view/config/config.php @@ -0,0 +1,90 @@ + + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() .'page/edit/' . $this->getUrl(0) , + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ helper::baseUrl() .$this->getUrl(0) . '/user' , + 'value' => 'Inscriptions' + ]); ?> +
+
+ +
+
+
+
+
+

Paramètres

+
+
+ 'Validité du lien', + 'selected' => $this->getData(['module','registration',$this->getUrl(0),'config','timeOut']) + ]); ?> +
+
+
+
+ getData(['page']), 'title', 'SORT_ASC'), [ + 'label' => 'Redirection après confirmation', + 'selected' => $this->getData(['module','registration',$this->getUrl(0),'config','pageSuccess']) + ]); ?> +
+
+ getData(['page']), 'title', 'SORT_ASC'), [ + 'label' => 'Redirection après erreur', + 'selected' => $this->getData(['module','registration',$this->getUrl(0),'config','pageError']) + ]); ?> +
+
+
+
+ Confirmez votre inscription en cliquant sur ce lien dans les ... minutes.

'; ?> + 'Corps du mail de confirmation', + 'value' => !empty($this->getData(['module','registration',$this->getUrl(0),'config','mailRegisterContent'])) ? $this->getData(['module','registration',$this->getUrl(0),'config','mailRegisterContent']) : $messageDefault, + 'class' => 'editorWysiwyg', + 'help' => 'Précisez la durée de validité. Le lien sera inséré après ces explications.' + ]); ?> +
+
+
+
+
+
+
+
+

Approbation préalable

+
+
+ $this->getData(['module','registration',$this->getUrl(0),'config','state']), + 'help' => 'Les comptes sont inactifs tant que les inscriptions ne sont pas approuvées par un administrateur.', + 'check' => true + ]); ?> +
+
+
+
+ Votre inscription a été approuvée par un administrateur.

'; ?> + 'Corps du mail d\'approbation', + 'value' =>!empty($this->getData(['module','registration',$this->getUrl(0),'config','mailValidateContent'])) ? $this->getData(['module','registration',$this->getUrl(0),'config','mailValidateContent']) : $messageDefault, + 'class' => 'editorWysiwyg' + ]); ?> +
+
+
+
+
+ +
Version n° +
diff --git a/module/registration/view/edit/edit.css b/module/registration/view/edit/edit.css new file mode 100644 index 00000000..3b94602b --- /dev/null +++ b/module/registration/view/edit/edit.css @@ -0,0 +1,16 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + + +@import url("core/layout/admin.css"); \ No newline at end of file diff --git a/module/registration/view/edit/edit.js.php b/module/registration/view/edit/edit.js.php new file mode 100644 index 00000000..ecb23d44 --- /dev/null +++ b/module/registration/view/edit/edit.js.php @@ -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 Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + +/** + * Droits des groupes + */ +$("#registrationUserEditGroup").on("change", function() { + $(".registrationUserEditGroupDescription").hide(); + $("#registrationUserEditGroupDescription" + $(this).val()).show(); +}).trigger("change"); diff --git a/module/registration/view/edit/edit.php b/module/registration/view/edit/edit.php new file mode 100644 index 00000000..08497aae --- /dev/null +++ b/module/registration/view/edit/edit.php @@ -0,0 +1,109 @@ + +
+
+ getUrl(3)): ?> + 'buttonGrey', + 'href' => helper::baseUrl() . $this->geturl(0) . '/user', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> + + 'buttonGrey', + 'href' => helper::baseUrl(false), + 'ico' => 'home', + 'value' => 'Accueil' + ]); ?> + +
+
+ +
+
+
+
+
+

Confirmation de l'inscription

+
+
+
+
+ 'off', + 'label' => 'Prénom', + 'value' => $this->getData(['user', $this->getUrl(2), 'firstname']), + 'disabled'=> true + ]); ?> +
+
+ 'off', + 'label' => 'Nom', + 'value' => $this->getData(['user', $this->getUrl(2), 'lastname']), + 'disabled'=> true + ]); ?> +
+
+
+
+ 'off', + 'label' => 'Adresse mail', + 'value' => $this->getData(['user', $this->getUrl(2), 'mail']), + 'disabled'=> true + ]); ?> +
+
+
+
+ getData(['user', $this->getUrl(2), 'group'])];?> + 'État de l\'inscription', + 'value' => $status, + 'disabled'=> true, + 'help' => 'En attente : le mail n\'a pas encore été validé
Email validé : approbation nécessaire.' + ]); ?> +
+
+ 'Date', + 'value' => utf8_encode( date('Y-m-d G:i', $this->getData(['user',$this->getUrl(2), 'timer']))), + 'disabled'=> true + ]); ?> +
+
+
+ +
+ getUser('group') === self::GROUP_ADMIN): ?> + ($this->getUrl(2) === $this->getUser('id')), + 'help' => ($this->getUrl(2) === $this->getUser('id') ? 'Impossible de modifier votre propre groupe.' : ''), + 'label' => 'Groupe (Banni : en attente d\'approbation)', + 'selected' => $groups[$this->getData(['user', $this->getUrl(2), 'group'])] + ]); ?> + Autorisations : +
    +
  • Accès aux pages privées membres
  • +
+
    +
  • Accès aux pages privées membres et éditeurs
  • +
  • Ajout / Édition / Suppression de pages
  • +
  • Ajout / Édition / Suppression de fichiers
  • +
+
    +
  • Accès à toutes les pages privées
  • +
  • Ajout / Édition / Suppression de pages
  • +
  • Ajout / Édition / Suppression de fichiers
  • +
  • Ajout / Édition / Suppression d'utilisateurs
  • +
  • Configuration du site
  • +
  • Personnalisation du thème
  • +
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/module/registration/view/index/index.js.php b/module/registration/view/index/index.js.php new file mode 100644 index 00000000..beb66a9f --- /dev/null +++ b/module/registration/view/index/index.js.php @@ -0,0 +1,48 @@ +/** + * 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 + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + + +/** + * Affichage de l'id en simulant FILTER_ID + */ +$("#registrationAddId").on("change keydown keyup", function(event) { + var userId = $(this).val(); + if( + event.keyCode !== 8 // BACKSPACE + && event.keyCode !== 37 // LEFT + && event.keyCode !== 39 // RIGHT + && event.keyCode !== 46 // DELETE + && window.getSelection().toString() !== userId // Texte sélectionné + ) { + var searchReplace = { + "á": "a", "à": "a", "â": "a", "ä": "a", "ã": "a", "å": "a", "ç": "c", "é": "e", "è": "e", "ê": "e", "ë": "e", "í": "i", "ì": "i", "î": "i", "ï": "i", "ñ": "n", "ó": "o", "ò": "o", "ô": "o", "ö": "o", "õ": "o", "ú": "u", "ù": "u", "û": "u", "ü": "u", "ý": "y", "ÿ": "y", + "Á": "A", "À": "A", "Â": "A", "Ä": "A", "Ã": "A", "Å": "A", "Ç": "C", "É": "E", "È": "E", "Ê": "E", "Ë": "E", "Í": "I", "Ì": "I", "Î": "I", "Ï": "I", "Ñ": "N", "Ó": "O", "Ò": "O", "Ô": "O", "Ö": "O", "Õ": "O", "Ú": "U", "Ù": "U", "Û": "U", "Ü": "U", "Ý": "Y", "Ÿ": "Y", + "'": "-", "\"": "-", " ": "-" + }; + userId = userId.replace(/[áàâäãåçéèêëíìîïñóòôöõúùûüýÿ'" ]/ig, function(match) { + return searchReplace[match]; + }); + userId = userId.replace(/[^a-z0-9-]/ig, ""); + $(this).val(userId); + } +}); + +/** + * Droits des groupes + */ +$("#registrationAddGroup").on("change", function() { + $(".registrationAddGroupDescription").hide(); + $("#registrationAddGroupDescription" + $(this).val()).show(); + console.log ($(this).val()); +}).trigger("change"); + + diff --git a/module/registration/view/index/index.php b/module/registration/view/index/index.php new file mode 100644 index 00000000..9ba78d50 --- /dev/null +++ b/module/registration/view/index/index.php @@ -0,0 +1,81 @@ + +
+
+
+

Identité

+
+
+ 'off', + 'label' => 'Prénom' + ]); ?> +
+
+ 'off', + 'label' => 'Nom' + ]); ?> +
+
+
+
+ 'off', + 'label' => 'Adresse mail' + ]); ?> +
+
+
+
+
+ self::GROUP_MEMBER + ]); ?> +
+
+ +
+

Données de connexion

+
+
+ 'off', + 'label' => 'Identifiant de connexion' + ]); ?> +
+
+
+
+ 'off', + 'label' => 'Mot de passe' + ]); ?> +
+
+
+
+ 'off', + 'label' => 'Confirmation du mot de passe' + ]); + ?> +
+
+
+
+
+ time() + ]); + ?> +
+
+
+
+
+ 'Envoyer' + ]); ?> +
+
+ diff --git a/module/registration/view/user/user.css b/module/registration/view/user/user.css new file mode 100644 index 00000000..3b94602b --- /dev/null +++ b/module/registration/view/user/user.css @@ -0,0 +1,16 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + + +@import url("core/layout/admin.css"); \ No newline at end of file diff --git a/module/registration/view/user/user.js.php b/module/registration/view/user/user.js.php new file mode 100644 index 00000000..f04544da --- /dev/null +++ b/module/registration/view/user/user.js.php @@ -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 + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + +/** + * Confirmation de suppression + */ +$(".registrationUserDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer cet utilisateur ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); \ No newline at end of file diff --git a/module/registration/view/user/user.php b/module/registration/view/user/user.php new file mode 100644 index 00000000..b01406bb --- /dev/null +++ b/module/registration/view/user/user.php @@ -0,0 +1,15 @@ +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'value' => 'Retour' + ]); ?> +
+
+ + + + + + \ No newline at end of file diff --git a/module/registration/view/validate/validate.php b/module/registration/view/validate/validate.php new file mode 100644 index 00000000..923a0a1e --- /dev/null +++ b/module/registration/view/validate/validate.php @@ -0,0 +1,2 @@ + + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + + +class sondage extends common { + + const VERSION = '1.0'; + const REALNAME = 'Sondage'; + const DELETE = true; + const UPDATE = true; + const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) + + public static $actions = [ + 'config' => self::GROUP_MODERATOR, + 'data' => self::GROUP_MODERATOR, + 'result' => self::GROUP_VISITOR, + 'delete' => self::GROUP_MODERATOR, + 'deleteall' => self::GROUP_MODERATOR, + 'index' => self::GROUP_VISITOR, + 'export2csv' => self::GROUP_MODERATOR, + 'output2csv' => self::GROUP_MODERATOR + ]; + + public static $data = []; + + public static $pages = []; + + public static $pagination; + + const TYPE_MAIL = 'mail'; + const TYPE_SELECT = 'select'; + const TYPE_TEXT = 'text'; + const TYPE_TEXTAREA = 'textarea'; + const TYPE_DATETIME = "date"; + const TYPE_CHECKBOX = "checkbox"; + + + public static $types = [ + self::TYPE_TEXT => 'Champ texte', + self::TYPE_TEXTAREA => 'Grand champ texte', + self::TYPE_MAIL => 'Champ mail', + self::TYPE_SELECT => 'Sélection', + self::TYPE_DATETIME => 'Date', + self::TYPE_CHECKBOX => 'Case à cocher' + ]; + + public static $listUsers = [ + ]; + + /** + * Configuration + */ + public function config() { + // Liste des utilisateurs + $userIdsFirstnames = helper::arrayCollumn($this->getData(['user']), 'firstname'); + ksort($userIdsFirstnames); + self::$listUsers [] = ''; + foreach($userIdsFirstnames as $userId => $userFirstname) { + self::$listUsers [] = $userId; + } + // Soumission du formulaire + if($this->isPost()) { + // Configuration + // Option sélectionnée sans page choisie + $this->setData([ + 'module', + $this->getUrl(0), + 'config', + [ + 'button' => $this->getInput('formConfigButton'), + 'capcha' => $this->getInput('formConfigCapcha', helper::FILTER_BOOLEAN), + 'group' => $this->getInput('formConfigGroup', helper::FILTER_INT), + 'user' => self::$listUsers [$this->getInput('formConfigUser', helper::FILTER_INT)], + 'mail' => $this->getInput('formConfigMail') , + 'pageId' => $this->getInput('formConfigPageIdToggle', helper::FILTER_BOOLEAN) === true ? $this->getInput('formConfigPageId', helper::FILTER_ID) : '', + 'subject' => $this->getInput('formConfigSubject') + ] + ]); + // Génération des données vides + $this->setData(['module', $this->getUrl(0), 'data', []]); + // Génération des champs + $inputs = []; + foreach($this->getInput('formConfigPosition', null) as $index => $position) { + $inputs[] = [ + 'name' => $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' => 'Modifications enregistrées', + 'redirect' => helper::baseUrl() . $this->getUrl(), + 'state' => true + ]); + } + // Liste des pages + foreach($this->getHierarchy(null, false) as $parentPageId => $childrenPageIds) { + self::$pages[$parentPageId] = $this->getData(['page', $parentPageId, 'title']); + foreach($childrenPageIds as $childKey) { + self::$pages[$childKey] = '    ' . $this->getData(['page', $childKey, 'title']); + } + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Configuration du module', + 'vendor' => [ + 'html-sortable', + 'flatpickr' + ], + 'view' => 'config' + ]); + } + + /** + * Données enregistrées + */ + public function data() { + $data = $this->getData(['module', $this->getUrl(0), 'data']); + if($data) { + // Pagination + $pagination = helper::pagination($data, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pagination = $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 . '
'; + } + self::$data[] = [ + $content, + template::button('formDataDelete' . $dataIds[$i], [ + 'class' => 'formDataDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/delete/' . $dataIds[$i] . '/' . $_SESSION['csrf'], + 'value' => template::ico('cancel') + ]) + ]; + } + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Données enregistrées', + 'view' => 'data' + ]); + } + + /** + * Réponses enregistrées + */ + public function result() { + $data = $this->getData(['module', $this->getUrl(0), 'data']); + if($data) { + // Pagination + $pagination = helper::pagination($data, $this->getUrl(),$this->getData(['config','itemsperPage'])); + // Liste des pages + self::$pagination = $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 . '
'; + } + self::$data[] = [ + $content + ]; + } + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Réponses', + 'view' => 'result' + ]); + } + + /** + * Export CSV + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + */ + public function export2csv() { + // Jeton incorrect + if ($this->getUrl(2) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data', + 'notification' => 'Action non autorisée' + ]); + } 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'); + } + $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' => 'Export CSV effectué dans le gestionnaire de fichiers
sous le nom '.$csvfilename, + 'redirect' => helper::baseUrl() . $this->getUrl(0) .'/data', + 'state' => true + ]); + } else { + $this->addOutput([ + 'notification' => 'Aucune donnée à exporter', + 'redirect' => helper::baseUrl() . $this->getUrl(0) .'/data' + ]); + } + } + } + + + /** + * Suppression + */ + public function deleteall() { + // Jeton incorrect + if ($this->getUrl(2) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data', + 'notification' => 'Action non autorisée' + ]); + } else { + $data = ($this->getData(['module', $this->getUrl(0), 'data'])); + if (count($data) > 0 ) { + // Suppression multiple + for ($i = 1; $i <= count($data) ; $i++) { + echo $this->deleteData(['module', $this->getUrl(0), 'data', $i]); + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data', + 'notification' => 'Données supprimées', + 'state' => true + ]); + } else { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data', + 'notification' => 'Aucune donnée à supprimer' + ]); + } + } + } + + + /** + * Suppression + */ + public function delete() { + // Jeton incorrect + if ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/data', + 'notification' => 'Action non autorisée' + ]); + } 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' => 'Donnée supprimée', + 'state' => true + ]); + } + } + } + + + + + /** + * Accueil + */ + public function index() { + // Soumission du formulaire + if($this->isPost()) { + // Check la capcha + if( + $this->getData(['module', $this->getUrl(0), 'config', 'capcha']) + AND $this->getInput('formCapcha', helper::FILTER_INT) !== $this->getInput('formCapchaFirstNumber', helper::FILTER_INT) + $this->getInput('formCapchaSecondNumber', helper::FILTER_INT)) + { + self::$inputNotices['formCapcha'] = 'Incorrect'; + + } + // Préparation le contenu du mail + $data = []; + $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; + } + $value = $this->getInput('formInput[' . $index . ']', $filter, $input['required']); + // 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 .= '' . $this->getData(['module', $this->getUrl(0), 'input', $index, 'name']) . ' : ' . $value . '
'; + } + // 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']) . '" :

' . + $content + ); + } + } + // Redirection + $redirect = $this->getData(['module', $this->getUrl(0), 'config', 'pageId']); + // Valeurs en sortie + $this->addOutput([ + 'notification' => ($sent === true ? 'Formulaire soumis' : $sent), + 'redirect' => $redirect ? helper::baseUrl() . $redirect : '', + 'state' => ($sent === true ? true : null) + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'showBarEditButton' => true, + 'showPageContent' => true, + 'view' => 'index', + 'vendor' => [ + 'flatpickr' + ], + ]); + } +} \ No newline at end of file diff --git a/module/sondage/vendor/html-sortable/html-sortable.min.js b/module/sondage/vendor/html-sortable/html-sortable.min.js new file mode 100644 index 00000000..9a96985e --- /dev/null +++ b/module/sondage/vendor/html-sortable/html-sortable.min.js @@ -0,0 +1 @@ +!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.sortable=t()}(this,function(){"use strict";function e(e,t,n){var r=null;return 0===t?e:function(){var a=n||this,o=arguments;clearTimeout(r),r=setTimeout(function(){e.apply(a,o)},t)}}var t,n,r,a=[],o=[],i=function(e,t,n){return void 0===n?e&&e.h5s&&e.h5s.data&&e.h5s.data[t]:(e.h5s=e.h5s||{},e.h5s.data=e.h5s.data||{},e.h5s.data[t]=n,void 0)},s=function(e){e.h5s&&delete e.h5s.data};switch(!0){case"matches"in window.Element.prototype:r="matches";break;case"mozMatchesSelector"in window.Element.prototype:r="mozMatchesSelector";break;case"msMatchesSelector"in window.Element.prototype:r="msMatchesSelector";break;case"webkitMatchesSelector"in window.Element.prototype:r="webkitMatchesSelector"}var l=function(e,t){if(!t)return Array.prototype.slice.call(e);for(var n=[],a=0;an){var c=o-n,f=p(e).top;if(id&&r>f+o-c)return}void 0===t.oldDisplay&&(t.oldDisplay=t.style.display),t.style.display="none",i + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + +/** + * 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(); +} + +/** + * 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 = getData(['module', $this->getUrl(0), 'input'])); ?>; +if(inputs) { + var inputsPerPosition = getData(['module', $this->getUrl(0), 'input']), 'position', 'SORT_ASC')); ?>; + $.each(inputsPerPosition, function(id) { + add(inputUid, inputs[id]); + inputUid++; + }); +} + +/** + * Afficher/cacher les options supplémentaires + */ +$(document).on("click", ".formConfigMoreToggle", function() { + $(this).parents(".formConfigInput").find(".formConfigMore").slideToggle(); +}); + +/** + * Crée un nouveau champ à partir des champs cachés + */ +$("#formConfigAdd").on("click", function() { + add(inputUid); + inputUid++; +}); + +/** + * Actions sur les champs + */ +// 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); + if(_this.val() === "select") { + _this.parents(".formConfigInput").find(".formConfigValuesWrapper").slideDown(); + } + else { + _this.parents(".formConfigInput").find(".formConfigValuesWrapper").slideUp(); + } + }); +// Simule un changement de type au chargement de la page +$(".formConfigType").trigger("change"); + +/** + * Affiche/cache les options de la case à cocher du mail + */ +$("#formConfigMailOptionsToggle").on("change", function() { + if($(this).is(":checked")) { + $("#formConfigMailOptions").slideDown(); + } + else { + $("#formConfigMailOptions").slideUp(function() { + $("#formConfigGroup").val(""); + $("#formConfigSubject").val(""); + $("#formConfigMail").val(""); + $("#formConfigUser").val(""); + }); + } +}).trigger("change"); + +/** + * Affiche/cache les options de la case à cocher de la redirection + */ +$("#formConfigPageIdToggle").on("change", function() { + if($(this).is(":checked")) { + $("#formConfigPageIdWrapper").slideDown(); + } + else { + $("#formConfigPageIdWrapper").slideUp(function() { + $("#formConfigPageId").val(""); + }); + } +}).trigger("change"); \ No newline at end of file diff --git a/module/sondage/view/config/config.php b/module/sondage/view/config/config.php new file mode 100644 index 00000000..8ded90fa --- /dev/null +++ b/module/sondage/view/config/config.php @@ -0,0 +1,155 @@ +
+
+ 'formConfigPosition' + ]); ?> +
+
+ template::ico('sort'), + 'class' => 'formConfigMove' + ]); ?> +
+
+ 'Intitulé' + ]); ?> +
+
+ 'formConfigType' + ]); ?> +
+
+ template::ico('gear'), + 'class' => 'formConfigMoreToggle' + ]); ?> +
+
+ template::ico('minus'), + 'class' => 'formConfigDelete' + ]); ?> +
+
+
+ 'Liste des valeurs séparées par des virgules (valeur1,valeur2,...)', + 'class' => 'formConfigValues', + 'classWrapper' => 'displayNone formConfigValuesWrapper' + ]); ?> + +
+
+
+ +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0), + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ helper::baseUrl() . $this->getUrl(0) . '/data', + 'value' => 'Gérer les données' + ]); ?> +
+
+ +
+
+
+
+
+

Configuration

+ 'Laissez vide afin de conserver le texte par défaut.', + 'label' => 'Texte du bouton de soumission', + 'value' => $this->getData(['module', $this->getUrl(0), 'config', 'button']) + ]); ?> + (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 saississez un email.' + ]); ?> +
+
+
+ 'Laissez vide afin de conserver le texte par défaut.', + 'label' => 'Sujet du mail', + 'value' => $this->getData(['module', $this->getUrl(0), 'config', 'subject']) + ]); ?> +
+
+ + Destinataires : +
+
+ 'Les groupes hiérarchiques à partir du groupe :', + 'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'group']), + 'help' => 'Editeurs = éditeurs + administrateurs
Membres = membres + éditeurs + administrateurs' + ]); ?> +
+
+
+
+ 'Un membre :', + 'selected' => array_search($this->getData(['module', $this->getUrl(0), 'config', 'user']),$module::$listUsers) + ]); ?> +
+
+
+
+ 'Une adresse email ou une liste de diffusion:', + 'value' => $this->getData(['module', $this->getUrl(0), 'config', 'mail']) + ]); ?> +
+
+
+ (bool) $this->getData(['module', $this->getUrl(0), 'config', 'pageId']) + ]); ?> +
+ 'displayNone', + 'label' => 'Sélectionner une page du site :', + 'selected' => $this->getData(['module', $this->getUrl(0), 'config', 'pageId']) + ]); ?> +
+ $this->getData(['module', $this->getUrl(0), 'config', 'capcha']) + ]); ?> +
+
+

Liste des champs

+
+ +
+
+
+
+ template::ico('plus') + ]); ?> +
+
+
+
+
+ +
Module version n° + +
diff --git a/module/sondage/view/data/data.js.php b/module/sondage/view/data/data.js.php new file mode 100644 index 00000000..91d0e044 --- /dev/null +++ b/module/sondage/view/data/data.js.php @@ -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 + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + +/** + * Confirmation de suppression + */ +$(".formDataDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer cette donnée ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Confirmation de suppression de toutes les donénes + */ +$(".formDataDeleteAll").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer toutes les données ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); \ No newline at end of file diff --git a/module/sondage/view/data/data.php b/module/sondage/view/data/data.php new file mode 100644 index 00000000..ae925983 --- /dev/null +++ b/module/sondage/view/data/data.php @@ -0,0 +1,28 @@ +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ 'formDataDeleteAll buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/deleteall' . '/' . $_SESSION['csrf'], + 'ico' => 'cancel', + 'value' => 'Tout effacer' + ]); ?> +
+
+ 'buttonBlue', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/export2csv' . '/' . $_SESSION['csrf'], + 'ico' => 'download', + 'value' => 'Export CSV' + ]); ?> +
+
+ + \ No newline at end of file diff --git a/module/sondage/view/index/index.js.php b/module/sondage/view/index/index.js.php new file mode 100644 index 00000000..699b1831 --- /dev/null +++ b/module/sondage/view/index/index.js.php @@ -0,0 +1,24 @@ +/** + * 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 + * @copyright Copyright (C) 2008-2018, Frédéric Tempez + * @license GNU General Public License, version 3 + * @link http://zwiicms.com/ + */ + +/** + * Paramétrage du format de date + */ +$(function() { + $(".datepicker").flatpickr({ + altInput: true, + altFormat: "d/m/Y", + enableTime: false, + locale: "fr", + dateFormat: "d/m/Y" + }); +}); diff --git a/module/sondage/view/index/index.php b/module/sondage/view/index/index.php new file mode 100644 index 00000000..ec6bf966 --- /dev/null +++ b/module/sondage/view/index/index.php @@ -0,0 +1,66 @@ +getData(['module', $this->getUrl(0), 'input'])): ?> + + getData(['module', $this->getUrl(0), 'input']) as $index => $input): ?> + + 'formInput_' . $index, + 'label' => $input['name'] + ]); ?> + + $key) { + $values[$value] = trim($value); + } + ?> + 'formInput_' . $index, + 'label' => $input['name'] + ]); ?> + + 'formInput_' . $index, + 'label' => $input['name'] + ]); ?> + + 'formInput_' . $index, + 'label' => $input['name'] + ]); ?> + + 'formInput_' . $index, + 'label' => $input['name'], + 'vendor' => 'flatpickr' + ]); ?> + + + + + getData(['module', $this->getUrl(0), 'config', 'capcha'])): ?> +
+
+ +
+
+ +
+
+ 'Réponses', + 'class' => 'buttonBlue', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/result' . '/' . $_SESSION['csrf'] + ]); ?> +
+
+ $this->getData(['module', $this->getUrl(0), 'config', 'button']) ? $this->getData(['module', $this->getUrl(0), 'config', 'button']) : 'Envoyer', + 'ico' => '' + ]); ?> +
+
+ + + + \ No newline at end of file diff --git a/module/sondage/view/result/result.php b/module/sondage/view/result/result.php new file mode 100644 index 00000000..864c812c --- /dev/null +++ b/module/sondage/view/result/result.php @@ -0,0 +1,12 @@ +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0), + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ + \ No newline at end of file diff --git a/module/version/version.php b/module/version/version.php new file mode 100644 index 00000000..88c0b223 --- /dev/null +++ b/module/version/version.php @@ -0,0 +1,30 @@ + * + * @copyright Copyright (C) 2018-2020, Frédéric Tempez + * @link http://zwiicms.com/ + */ + +class version extends common { + + public static $actions = [ + 'index'=> self::GROUP_VISITOR + + ]; + + /** + * Retourne le numéro de version + */ + + public function index() { + exit( common::ZWII_VERSION); + } + +} diff --git a/module/version/view/index/index.php b/module/version/view/index/index.php new file mode 100644 index 00000000..2263730f --- /dev/null +++ b/module/version/view/index/index.php @@ -0,0 +1 @@ + Date: Wed, 3 Mar 2021 14:24:15 +0100 Subject: [PATCH 10/18] auto inscription --- module/registration/registration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/registration/registration.php b/module/registration/registration.php index c0a35d83..560aca34 100644 --- a/module/registration/registration.php +++ b/module/registration/registration.php @@ -15,7 +15,7 @@ class registration extends common { const VERSION = '1.0'; - const REALNAME = 'Enregistrement'; + const REALNAME = 'Auto-Inscription'; const DELETE = true; const UPDATE = true; const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) From 5ac2b33a2d92a361a79698c53469056e3455e207 Mon Sep 17 00:00:00 2001 From: fredtempez Date: Thu, 4 Mar 2021 13:30:38 +0100 Subject: [PATCH 11/18] constante update --- module/download/download.php | 2 +- module/registration/registration.php | 2 +- module/sondage/sondage.php | 2 +- module/version/version.php | 12 +++++++++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/module/download/download.php b/module/download/download.php index f7c97f2a..4c006721 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -17,7 +17,7 @@ class download extends common { const VERSION = '1.0'; const REALNAME = 'Téléchargement'; const DELETE = true; - const UPDATE = true; + const UPDATE = '0.0'; const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) const EDIT_OWNER = 'owner'; diff --git a/module/registration/registration.php b/module/registration/registration.php index 560aca34..65d439cf 100644 --- a/module/registration/registration.php +++ b/module/registration/registration.php @@ -17,7 +17,7 @@ class registration extends common { const VERSION = '1.0'; const REALNAME = 'Auto-Inscription'; const DELETE = true; - const UPDATE = true; + const UPDATE = '0.0'; const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) const STATUS_AWAITING = NULL; // En attente de validation du mail diff --git a/module/sondage/sondage.php b/module/sondage/sondage.php index dcbf6d52..6d41feb0 100644 --- a/module/sondage/sondage.php +++ b/module/sondage/sondage.php @@ -20,7 +20,7 @@ class sondage extends common { const VERSION = '1.0'; const REALNAME = 'Sondage'; const DELETE = true; - const UPDATE = true; + const UPDATE = '0.0'; const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) public static $actions = [ diff --git a/module/version/version.php b/module/version/version.php index 88c0b223..d851a457 100644 --- a/module/version/version.php +++ b/module/version/version.php @@ -7,24 +7,30 @@ * file that was distributed with this source code. * * @license GNU General Public License, version 3 - * @author : Frédéric Tempez * + * @author : Frédéric Tempez * * @copyright Copyright (C) 2018-2020, Frédéric Tempez * @link http://zwiicms.com/ */ class version extends common { + const VERSION = '1.0'; + const REALNAME = 'Version'; + const DELETE = true; + const UPDATE = '0.0'; + const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) + public static $actions = [ 'index'=> self::GROUP_VISITOR ]; - + /** * Retourne le numéro de version */ public function index() { exit( common::ZWII_VERSION); - } + } } From 7dc0b4caf8aacf870d8942d72e278e6fd9add41b Mon Sep 17 00:00:00 2001 From: fredtempez Date: Fri, 5 Mar 2021 18:33:03 +0100 Subject: [PATCH 12/18] liste les modules en json --- module/download/download.php | 24 +++++++++++++++++++++++- module/download/view/list/list.php | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 module/download/view/list/list.php diff --git a/module/download/download.php b/module/download/download.php index 4c006721..ac21653e 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -37,7 +37,8 @@ class download extends common { 'statsDeleteAll' => self::GROUP_MODERATOR, 'index' => self::GROUP_VISITOR, 'rss' => self::GROUP_VISITOR, - 'downloadFile' => self::GROUP_VISITOR + 'downloadFile' => self::GROUP_VISITOR, + 'list' =>self::GROUP_VISITOR ]; public static $items = []; @@ -904,4 +905,25 @@ class download extends common { ]); } } + + /*** + * Retourne une chaîne json contenant la liste des téléchargements disponibles + */ + public function list() { + $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0),'items']), 'publishedOn', 'SORT_DESC'); + $itemIdsStates = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'state', 'SORT_DESC'); + $itemIds = []; + foreach($itemIdsPublishedOns as $itemId => $itemPublishedOn) { + if($itemPublishedOn <= time() AND $itemIdsStates[$itemId]) { + $itemIds[] = $itemId; + } + } + foreach ($itemIds as $key) { + self::$items[$key] = $this->getData(['module', $this->getUrl(0), 'items', $key]); + } + $this->addOutput([ + 'display' => self::DISPLAY_JSON, + 'content' => self::$items + ]); + } } \ No newline at end of file diff --git a/module/download/view/list/list.php b/module/download/view/list/list.php new file mode 100644 index 00000000..4b8f6d3c --- /dev/null +++ b/module/download/view/list/list.php @@ -0,0 +1 @@ + \ No newline at end of file From 9b6f8f1c118797fb371a012b0962c97ebffe7aff Mon Sep 17 00:00:00 2001 From: fredtempez Date: Sat, 6 Mar 2021 17:40:35 +0100 Subject: [PATCH 13/18] Download limit json rest api --- module/download/download.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/module/download/download.php b/module/download/download.php index ac21653e..bcebbf77 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -919,7 +919,14 @@ class download extends common { } } foreach ($itemIds as $key) { - self::$items[$key] = $this->getData(['module', $this->getUrl(0), 'items', $key]); + self::$items[$key] = [ + $this->getData(['module', $this->getUrl(0), 'items', $key, 'title']), + $this->getData(['module', $this->getUrl(0), 'items', $key, 'content']), + $this->getData(['module', $this->getUrl(0), 'items', $key, 'picture']), + $this->getData(['module', $this->getUrl(0), 'items', $key, 'file']), + $this->getData(['module', $this->getUrl(0), 'items', $key, 'fileVersion']), + $this->getData(['module', $this->getUrl(0), 'items', $key, 'fileDate']) + ]; } $this->addOutput([ 'display' => self::DISPLAY_JSON, From ab05c7a7f7a52f02921fd4166a843436b0a25765 Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Mon, 8 Mar 2021 18:33:53 +0100 Subject: [PATCH 14/18] =?UTF-8?q?Transf=C3=A9rer=20les=20fichiers=20vers?= =?UTF-8?q?=20'module/download'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/download/download.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/module/download/download.php b/module/download/download.php index bcebbf77..66011dd5 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -920,12 +920,12 @@ class download extends common { } foreach ($itemIds as $key) { self::$items[$key] = [ - $this->getData(['module', $this->getUrl(0), 'items', $key, 'title']), - $this->getData(['module', $this->getUrl(0), 'items', $key, 'content']), - $this->getData(['module', $this->getUrl(0), 'items', $key, 'picture']), - $this->getData(['module', $this->getUrl(0), 'items', $key, 'file']), - $this->getData(['module', $this->getUrl(0), 'items', $key, 'fileVersion']), - $this->getData(['module', $this->getUrl(0), 'items', $key, 'fileDate']) + 'title' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'title']), + 'content' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'content']), + 'picture' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'picture']), + 'file' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'file']), + 'fileVersion' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'fileVersion']), + 'filetdate' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'fileDate']) ]; } $this->addOutput([ From c30b839587b85d4265fe2dfcdc0de09bc46f30c9 Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Mon, 8 Mar 2021 18:34:34 +0100 Subject: [PATCH 15/18] =?UTF-8?q?Transf=C3=A9rer=20les=20fichiers=20vers?= =?UTF-8?q?=20'module/download'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/download/download.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/download/download.php b/module/download/download.php index 66011dd5..10533d62 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -925,7 +925,7 @@ class download extends common { 'picture' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'picture']), 'file' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'file']), 'fileVersion' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'fileVersion']), - 'filetdate' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'fileDate']) + 'fileDate' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'fileDate']) ]; } $this->addOutput([ From 3a80515b17e9cff32866be8f519d6aac8718bfdf Mon Sep 17 00:00:00 2001 From: fredtempez Date: Sun, 14 Mar 2021 14:42:37 +0100 Subject: [PATCH 16/18] =?UTF-8?q?Licene=20et=20auteur=20+=20nouvelle=20pr?= =?UTF-8?q?=C3=A9sentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/download/download.php | 13 + module/download/view/add/add.php | 14 ++ module/download/view/edit/edit.php | 14 ++ module/download/view/item/item.js.php | 22 +- module/download/view/item/item.php | 343 ++++++++++++++------------ 5 files changed, 234 insertions(+), 172 deletions(-) diff --git a/module/download/download.php b/module/download/download.php index 10533d62..b63d067f 100644 --- a/module/download/download.php +++ b/module/download/download.php @@ -20,6 +20,7 @@ class download extends common { const UPDATE = '0.0'; const DATADIRECTORY = []; // Contenu localisé inclus par défaut (page.json et module.json) + // Constantes du module const EDIT_OWNER = 'owner'; const EDIT_GROUP = 'group'; const EDIT_ALL = 'all'; @@ -98,6 +99,14 @@ class download extends common { ]; + public static $itemLicense = [ + 'none'=> 'Non définie', + 'cc' => 'Licence libre Creative Common, partage autorisé', + 'gnu' => 'Licence libre GNU, partage autorisé', + 'mit' => 'Licence libre MIT, partage autorisé', + 'owner' => 'Licence Propriétaire' + ]; + public static $users = []; /** @@ -189,6 +198,8 @@ class download extends common { 'file' => $this->getInput('downloadAddFile', helper::FILTER_STRING_SHORT, true), 'fileVersion' => $this->getInput('downloadAddFileVersion', helper::FILTER_STRING_SHORT, true), 'fileDate' => $this->getInput('downloadAddFileDate', helper::FILTER_DATETIME, true), + 'fileLicense' => $this->getInput('downloadAddFileLicense', helper::FILTER_STRING_SHORT, true), + 'fileAuthor' => $this->getInput('downloadAddFileAuthor', helper::FILTER_STRING_SHORT, true), 'fileStats' => [], 'publishedOn' => $this->getInput('downloadAddPublishedOn', helper::FILTER_DATETIME, true), 'state' => $this->getInput('downloadAddState', helper::FILTER_BOOLEAN), @@ -555,6 +566,8 @@ class download extends common { 'fileVersion' => $this->getInput('downloadEditFileVersion', helper::FILTER_STRING_SHORT, true), 'fileDate' => $this->getInput('downloadEditFileDate', helper::FILTER_DATETIME, true), 'fileStats' => $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'fileStats']), + 'fileLicense' => $this->getInput('downloadEditFileLicense', helper::FILTER_STRING_SHORT, true), + 'fileAuthor' => $this->getInput('downloadEditFileAuthor', helper::FILTER_STRING_SHORT, true), 'publishedOn' => $this->getInput('downloadEditPublishedOn', helper::FILTER_DATETIME, true), 'state' => $this->getInput('downloadEditState', helper::FILTER_BOOLEAN), 'title' => $this->getInput('downloadEditTitle', helper::FILTER_STRING_SHORT, true), diff --git a/module/download/view/add/add.php b/module/download/view/add/add.php index a4df7f10..b392a5ec 100644 --- a/module/download/view/add/add.php +++ b/module/download/view/add/add.php @@ -57,6 +57,20 @@ ]); ?>
+
+
+ 'Auteur', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileAuthor']) + ]); ?> +
+
+ 'Licence', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileLicense']) + ]); ?> +
+
diff --git a/module/download/view/edit/edit.php b/module/download/view/edit/edit.php index 7dc32b55..d80153ad 100644 --- a/module/download/view/edit/edit.php +++ b/module/download/view/edit/edit.php @@ -64,6 +64,20 @@ ]); ?> +
+
+ 'Auteur', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileAuthor']) + ]); ?> +
+
+ 'Licence', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'fileLicense']) + ]); ?> +
+
diff --git a/module/download/view/item/item.js.php b/module/download/view/item/item.js.php index 2748a7c8..3cda4b73 100644 --- a/module/download/view/item/item.js.php +++ b/module/download/view/item/item.js.php @@ -13,31 +13,31 @@ /** * Affiche le bloc pour rédiger un commentaire */ -var commentShowDOM = $("#downloaditemCommentShow"); +var commentShowDOM = $("#downloadItemCommentShow"); commentShowDOM.on("click focus", function() { - $("#downloaditemCommentShowWrapper").fadeOut(function() { - $("#downloaditemCommentWrapper").fadeIn(); - $("#downloaditemCommentContent").trigger("focus"); + $("#downloadItemCommentShowWrapper").fadeOut(function() { + $("#downloadItemCommentWrapper").fadeIn(); + $("#downloadItemCommentContent").trigger("focus"); }); }); -if($("#downloaditemCommentWrapper").find("textarea.notice,input.notice").length) { +if($("#downloadItemCommentWrapper").find("textarea.notice,input.notice").length) { commentShowDOM.trigger("click"); } /** * Cache le bloc pour rédiger un commentaire */ -$("#downloaditemCommentHide").on("click focus", function() { - $("#downloaditemCommentWrapper").fadeOut(function() { - $("#downloaditemCommentShowWrapper").fadeIn(); - $("#downloaditemCommentContent").val(""); - $("#downloaditemCommentAuthor").val(""); +$("#downloadItemCommentHide").on("click focus", function() { + $("#downloadItemCommentWrapper").fadeOut(function() { + $("#downloadItemCommentShowWrapper").fadeIn(); + $("#downloadItemCommentContent").val(""); + $("#downloadItemCommentAuthor").val(""); }); }); /** * Force le scroll vers les commentaires en cas d'erreur */ -$("#downloaditemCommentForm").on("submit", function() { +$("#downloadItemCommentForm").on("submit", function() { $(location).attr("href", "#comment"); }); \ No newline at end of file diff --git a/module/download/view/item/item.php b/module/download/view/item/item.php index 3173dbf9..6b1a7e10 100644 --- a/module/download/view/item/item.php +++ b/module/download/view/item/item.php @@ -1,27 +1,34 @@ -
-
-
-
+
+
+
+
+ getData(['module', $this->getUrl(0),'items', $this->getUrl(1), 'content']); ?> +
+
+
+
+ + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); - $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) - ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) + $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) + ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); - echo $date . ' à ' . $heure; + echo $date . ' à ' . $heure; ?> +
-
-
- + getUser('password') === $this->getInput('ZWII_USER_PASSWORD') AND ( // Propriétaire ( - $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_OWNER - AND ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'userId']) === $this->getUser('id') - OR $this->getUser('group') === self::GROUP_ADMIN ) + $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'editConsent']) === $module::EDIT_OWNER + AND ( $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'userId']) === $this->getUser('id') + OR $this->getUser('group') === self::GROUP_ADMIN ) ) OR ( // Groupe @@ -38,158 +45,172 @@ ): ?> helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], - 'value' => 'Editer' + 'value' => template::ico('pencil') ]); ?> - -
-
-
-
-
-
- getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']); ?> - getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . - '" alt="' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . '">'; - ?> -
-
-
-
- self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file']), - 'href' => helper::baseUrl() . $this->getUrl(0) . '/downloadFile/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], - 'value' => 'Télécharger' - ]); ?> -
-
-
-
- getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileVersion']); ?> -
-
-
-
- getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])), 'UTF-8', true) - ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])) - : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate']))); - ?> - -
-
-
-
- Téléchargements : - - -
+
-
- getData(['module', $this->getUrl(0),'items', $this->getUrl(1), 'content']); ?> -
-
-

-
-
- getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> -
- - getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentClose'])): ?> -

Cet item ne reçoit pas de commentaire.

- -

- - - - 0 ? $commentsNb . ' ' . 'commentaire' . $s : 'Pas encore de commentaire'; ?> -

- - 'Rédiger un commentaire...', - 'readonly' => true - ]); ?> -
- getUser('password') === $this->getInput('ZWII_USER_PASSWORD')): ?> - 'Nom', - 'readonly' => true, - 'value' => $module::$editCommentSignature - ]); ?> - $this->getUser('id') - ]); ?> - -
-
- 'Nom' - ]); ?> -
-
-
Ou
-
-
- helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl()) . '__comment', - 'value' => 'Connexion' - ]); ?> -
-
- - 'Commentaire avec maximum '.$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']).' caractères', - 'class' => 'editorWysiwygComment', - 'noDirty' => true, - 'maxlength' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']) - ]); ?> -
- getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')): ?> -
-
- $this->getData(['config','captchaStrong']) - ]); ?> -
-
- -
-
- 'buttonGrey', - 'value' => 'Annuler' - ]); ?> -
-
- 'Envoyer', - 'ico' => '' - ]); ?> -
-
-
- -
-
- $comment): ?> -
-

- le +
+ getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + + + ' . $this->getData(['module',$this->getUrl(0), 'config', 'feedsLabel']) . '

' ; ?> - -
- + + +

- -
\ No newline at end of file +
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']); ?> + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . + '" alt="' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'picture']) . '">'; + ?> +
+
+
+
+ self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file']), + 'href' => helper::baseUrl() . $this->getUrl(0) . '/downloadFile/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], + 'value' => 'Télécharger' + ]); ?> +
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileVersion']); ?> +
+
+
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileDate']))); + ?> + +
+
+
+
+ Auteur : + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileAuthor']); ?> + +
+
+
+
+ Licence : + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'fileLicense'])]; ?> + +
+
+
+
+ Téléchargements : + + +
+
+
+ + +
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentClose'])): ?> +

Cet item ne reçoit pas de commentaire.

+ +

+ + + + 0 ? $commentsNb . ' ' . 'commentaire' . $s : 'Pas encore de commentaire'; ?> +

+ + 'Rédiger un commentaire...', + 'readonly' => true + ]); ?> +
+ getUser('password') === $this->getInput('ZWII_USER_PASSWORD')): ?> + 'Nom', + 'readonly' => true, + 'value' => $module::$editCommentSignature + ]); ?> + $this->getUser('id') + ]); ?> + +
+
+ 'Nom' + ]); ?> +
+
+
Ou
+
+
+ helper::baseUrl() . 'user/login/' . str_replace('/', '_', $this->getUrl()) . '__comment', + 'value' => 'Connexion' + ]); ?> +
+
+ + 'Commentaire avec maximum '.$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']).' caractères', + 'class' => 'editorWysiwygComment', + 'noDirty' => true, + 'maxlength' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'commentMaxlength']) + ]); ?> +
+ getUser('password') !== $this->getInput('ZWII_USER_PASSWORD')): ?> +
+
+ $this->getData(['config','captchaStrong']) + ]); ?> +
+
+ +
+
+ 'buttonGrey', + 'value' => 'Annuler' + ]); ?> +
+
+ 'Envoyer', + 'ico' => '' + ]); ?> +
+
+
+ +
+
+ $comment): ?> +
+

+ le + +

+ +
+
+
+
+ \ No newline at end of file From 154278ef9824b6a4a97c3536ef55c3962defdadf Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Mon, 15 Mar 2021 16:40:29 +0100 Subject: [PATCH 17/18] modules blog et download WIP --- core/core.php | 2 +- module/blog/view/article/article.css | 1 + module/blog/view/article/article.php | 115 ++++++++++++++------------- module/download/view/item/item.php | 27 +++---- 4 files changed, 75 insertions(+), 70 deletions(-) diff --git a/core/core.php b/core/core.php index b915ca85..c1c8e23d 100755 --- a/core/core.php +++ b/core/core.php @@ -812,7 +812,7 @@ class common { $sitemap->updateRobots(); // Submit your sitemaps to Google, Yahoo, Bing and Ask.com - $sitemap->submitSitemap(); + //$sitemap->submitSitemap(); return(file_exists('sitemap.xml')); diff --git a/module/blog/view/article/article.css b/module/blog/view/article/article.css index 763f8384..bec76899 100755 --- a/module/blog/view/article/article.css +++ b/module/blog/view/article/article.css @@ -45,6 +45,7 @@ font-size:0.8em; font-style: italic; color: grey; + height: 100%; } @media (max-width: 767px) { .blogArticlePicture { diff --git a/module/blog/view/article/article.php b/module/blog/view/article/article.php index aa19fe55..2e07f34d 100755 --- a/module/blog/view/article/article.php +++ b/module/blog/view/article/article.php @@ -1,66 +1,73 @@
-
-
- - getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) - ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])) - : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn']))); - $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) - ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])) - : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn']))); - echo $date . ' à ' . $heure; - ?> -
+
+ getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'pictureSize']); ?> + getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'hidePicture']) == false) { + echo '' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'picture']) . ''; + } ?> + getData(['module', $this->getUrl(0),'posts', $this->getUrl(1), 'content']); ?>
-
+
+
+
+ + + + getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn']))); + $heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) + ? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn'])) + : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn']))); + echo $date . ' à ' . $heure; + ?> + + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + + +
+
getUser('password') === $this->getInput('ZWII_USER_PASSWORD') - 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 ) + + $this->getUser('password') === $this->getInput('ZWII_USER_PASSWORD') + 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_MODERATOR) + 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'] ) - 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_MODERATOR) - 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'] - ) - ) - ): ?> + ) + ): ?> + + + helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], - 'value' => 'Editer' - ]); ?> + 'value' => template::ico('pencil') + ]); ?>
- getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'pictureSize']); ?> - getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'hidePicture']) == false) { - echo '' . $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'picture']) . ''; - } ?> -getData(['module', $this->getUrl(0),'posts', $this->getUrl(1), 'content']); ?> -

- -getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> - - getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'commentClose'])): ?>

Cet article ne reçoit pas de commentaire.

diff --git a/module/download/view/item/item.php b/module/download/view/item/item.php index 6b1a7e10..2eeba01c 100644 --- a/module/download/view/item/item.php +++ b/module/download/view/item/item.php @@ -9,7 +9,6 @@
- getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])) : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); @@ -18,9 +17,20 @@ : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); echo $date . ' à ' . $heure; ?> + + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + +
-
+
getUser('password') === $this->getInput('ZWII_USER_PASSWORD') AND @@ -50,19 +60,6 @@
- -
-
- getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> - - - ' . $this->getData(['module',$this->getUrl(0), 'config', 'feedsLabel']) . '

' ; - ?> -
- -
-
From f8887806a07eef84583464c4aa5c8ecdf49255cf Mon Sep 17 00:00:00 2001 From: Fred Tempez Date: Mon, 15 Mar 2021 16:46:59 +0100 Subject: [PATCH 18/18] =?UTF-8?q?Bouton=20=C3=A9dition=20l=C3=A9ger=20blog?= =?UTF-8?q?=20et=20download?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module/blog/view/article/article.php | 33 +++++++++++-------------- module/download/view/item/item.php | 36 +++++++++++++--------------- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/module/blog/view/article/article.php b/module/blog/view/article/article.php index 2e07f34d..62263c9a 100755 --- a/module/blog/view/article/article.php +++ b/module/blog/view/article/article.php @@ -10,7 +10,7 @@
-
+
@@ -22,19 +22,7 @@ : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'posts', $this->getUrl(1), 'publishedOn']))); echo $date . ' à ' . $heure; ?> - - getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> - - -
-
+ getUser('password') === $this->getInput('ZWII_USER_PASSWORD') @@ -59,12 +47,19 @@ ) ): ?> - + Editer - helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], - 'value' => template::ico('pencil') - ]); ?> + + + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> +
diff --git a/module/download/view/item/item.php b/module/download/view/item/item.php index 2eeba01c..5831143b 100644 --- a/module/download/view/item/item.php +++ b/module/download/view/item/item.php @@ -6,7 +6,7 @@
-
+
getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn'])), 'UTF-8', true) @@ -17,20 +17,8 @@ : utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'publishedOn']))); echo $date . ' à ' . $heure; ?> - - getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> - - + -
-
getUser('password') === $this->getInput('ZWII_USER_PASSWORD') AND @@ -53,11 +41,21 @@ ) ) ): ?> - helper::baseUrl() . $this->getUrl(0) . '/edit/' . $this->getUrl(1) . '/' . $_SESSION['csrf'], - 'value' => template::ico('pencil') - ]); ?> - + + Editer + + + + getData(['module',$this->getUrl(0), 'config', 'feeds'])): ?> + +