From 4890b04bafa2286b62bf61fd60faba844ee2729c Mon Sep 17 00:00:00 2001 From: fredtempez Date: Fri, 18 Mar 2022 14:15:21 +0100 Subject: [PATCH] plufin WIP + download en test --- core/module/plugin/plugin.php | 122 +- module/download/download.php | 1236 +++++++++++++++++ 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 | 183 +++ .../download/view/categories/categories.css | 38 + .../view/categories/categories.js.php | 70 + .../download/view/categories/categories.php | 50 + .../view/categoryEdit/categoryEdit.css | 18 + .../view/categoryEdit/categoryEdit.php | 31 + 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 | 37 + module/download/view/config/config.js.php | 68 + module/download/view/config/config.php | 75 + module/download/view/edit/edit.css | 18 + module/download/view/edit/edit.js.php | 77 + module/download/view/edit/edit.php | 193 +++ module/download/view/index/index.css | 69 + module/download/view/index/index.php | 67 + module/download/view/item/item.css | 67 + module/download/view/item/item.js.php | 50 + module/download/view/item/item.php | 235 ++++ module/download/view/list/list.php | 1 + module/download/view/rss/rss.php | 1 + module/download/view/stats/stats.css | 18 + module/download/view/stats/stats.js.php | 22 + module/download/view/stats/stats.php | 36 + 37 files changed, 4526 insertions(+), 47 deletions(-) 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/categories/categories.css create mode 100644 module/download/view/categories/categories.js.php create mode 100644 module/download/view/categories/categories.php create mode 100644 module/download/view/categoryEdit/categoryEdit.css create mode 100644 module/download/view/categoryEdit/categoryEdit.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/list/list.php create mode 100644 module/download/view/rss/rss.php create mode 100644 module/download/view/stats/stats.css create mode 100644 module/download/view/stats/stats.js.php create mode 100644 module/download/view/stats/stats.php diff --git a/core/module/plugin/plugin.php b/core/module/plugin/plugin.php index 5a128af8..37608af9 100644 --- a/core/module/plugin/plugin.php +++ b/core/module/plugin/plugin.php @@ -9,9 +9,7 @@ * @author Rémi Jean * @copyright Copyright (C) 2008-2018, Rémi Jean * @author Frédéric Tempez - * @copyright Copyright (C) 2020-2021, Sylvain Lelièvre * @copyright Copyright (C) 2018-2022, Frédéric Tempez - * @author Sylvain Lelièvre * @license GNU General Public License, version 3 * @link http://zwiicms.fr/ */ @@ -100,51 +98,82 @@ class plugin extends common { * Installation d'un module * Fonction utilisée par upload et storeUpload */ - private function install ($moduleName, $checkValid){ - $tempFolder = 'datamodules';//uniqid(); - $zip = new ZipArchive(); - if ($zip->open($moduleName) === TRUE) { - $notification = 'Archive ouverte'; - mkdir (self::TEMP_DIR . $tempFolder, 0755); - $zip->extractTo(self::TEMP_DIR . $tempFolder ); - // Archive de module ? - $success = false; - $notification = 'Ce n\'est pas l\'archive d\'un module !'; - $moduleDir = self::TEMP_DIR . $tempFolder . '/module'; - $moduleName = ''; - if ( is_dir( $moduleDir )) { - // Lire le nom du module - if ($dh = opendir( $moduleDir )) { - while ( false !== ($file = readdir($dh)) ) { - if ($file != "." && $file != "..") { - $moduleName = $file; - } - } - closedir($dh); - } - // Module normalisé ? - if( is_file( $moduleDir.'/'.$moduleName.'/'.$moduleName.'.php' ) AND is_file( $moduleDir.'/'.$moduleName.'/view/index/index.php' ) ){ + private function install($moduleFileName, $checkValid){ - // Lecture de la version et de la validation d'update du module pour validation de la mise à jour - // Pour une version <= version installée l'utilisateur doit cocher 'Mise à jour forcée' - $version = '0.0'; - $update = '0.0'; - $valUpdate = false; - $file = file_get_contents( $moduleDir.'/'.$moduleName.'/'.$moduleName.'.php'); - $file = str_replace(' ','',$file); - $file = str_replace("\t",'',$file); - $pos1 = strpos($file, 'constVERSION'); - if( $pos1 !== false){ - $posdeb = strpos($file, "'", $pos1); - $posend = strpos($file, "'", $posdeb + 1); - $version = substr($file, $posdeb + 1, $posend - $posdeb - 1); - } - $pos1 = strpos($file, 'constUPDATE'); - if( $pos1 !== false){ - $posdeb = strpos($file, "'", $pos1); - $posend = strpos($file, "'", $posdeb + 1); - $update = substr($file, $posdeb + 1, $posend - $posdeb - 1); - } + /** + * Initialisation des variables + */ + //$tempFolder = uniqid() . '/'; + $tempFolder = 'truc/'; + $notification = ''; + // Numéro de version du module déjà installé + $versionInstalled = ''; + + /** + * Désarchivage + */ + $zip = new ZipArchive(); + if ($zip->open($moduleFileName) === TRUE) { + + /** + * Création du dossier temporaire + */ + if (!is_dir (self::TEMP_DIR . $tempFolder) ) { + mkdir (self::TEMP_DIR . $tempFolder, 0755); + } + + $zip->extractTo(self::TEMP_DIR . $tempFolder ); + // Lecture du descripteur de ressource + $modulePath = self::TEMP_DIR . $tempFolder . 'module'; + if (file_exists(self::TEMP_DIR . $tempFolder . 'desc.json') + ) { + $res = json_decode(file_get_contents(self::TEMP_DIR . $tempFolder . 'desc.json'), true); + } else { + return([ + 'success' => false, + 'notification'=> 'Cette archive est invalide' + ]); + } + /** + * Structure lue + * $res ['name'] = id du module, correspond à la classe + * $res ['realname'] = Nom complet du module + * $res ['version'] = version du module + * $res ['dirs'] @array + * 'dossier' => 'destination', + * 'download" => 'module/download' + * + * Attention le dossier source est celui dans l'archive, la destination correspond à l’arborescence cible + */ + echo "
";
+			// Affectation
+			$moduleName = $res ['name'];
+			$modulePath = $res ['name'] . '/';
+			$moduleFile = $res ['name'] . '.php' ;
+
+			/**
+			 * Validation des informations du descripteur
+			 */
+			if ( !is_dir ( self::TEMP_DIR . $tempFolder . $modulePath) ||
+				 !is_file (self::TEMP_DIR . $tempFolder . $modulePath . $moduleFile)
+				) {
+					return([
+						'success' => false,
+						'notification'=> 'Cette archive est invalide'
+					]);
+			}
+			/**
+			 * Le module est-il installé ? Lire le numéro de version
+			 */
+			if (is_file( 'module/' . $modulePath . $moduleFile) ) {
+				$c = helper::getModules();
+				if (array_key_exists($moduleName, $c)) {
+					$versionInstalled = $c[$moduleName]['version'];
+				}
+			}
+			echo $versionInstalled;
+
+			die();
 					// Si version actuelle >= version indiquée dans UPDATE la mise à jour est validée
 					$infoModules = helper::getModules();
 					if( $infoModules[$moduleName]['update'] >= $update ) $valUpdate = true;
@@ -195,7 +224,6 @@ class plugin extends common {
 						}
 					}
 				}
-			}
 			// Supprimer le dossier temporaire même si le module est invalide
 			$this->removeDir(self::TEMP_DIR . $tempFolder);
 			$zip->close();
diff --git a/module/download/download.php b/module/download/download.php
new file mode 100644
index 00000000..f615f2b4
--- /dev/null
+++ b/module/download/download.php
@@ -0,0 +1,1236 @@
+
+ * @copyright Copyright (C) 2008-2018, Rémi Jean
+ * @license GNU General Public License, version 3
+ * @link http://zwiicms.fr/
+ */
+
+class download extends common {
+
+	const VERSION = '2.5';
+	const REALNAME = 'Téléchargement';
+	const DELETE = true;
+	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';
+
+	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,
+		'statsDeleteAll' => self::GROUP_MODERATOR,
+		'categories' => self::GROUP_MODERATOR,
+		'categoryEdit' => self::GROUP_MODERATOR,
+		'categoryDelete' => self::GROUP_MODERATOR,
+		'index' => self::GROUP_VISITOR,
+		'rss' => self::GROUP_VISITOR,
+		'downloadFile' => self::GROUP_VISITOR,
+		'list' =>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;
+
+	// Nombre de téléchargements
+	public static $statSum = 0;
+
+	public static $states = [
+		false => 'Brouillon',
+		true => 'Publié'
+	];
+
+	// Liste des catégories
+	public static $categories = [];
+
+	public static $allCategories = '';
+
+	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'
+	];
+
+	// Nombre d'objets par page
+	public static $ItemsList = [
+		4 => '4 articles',
+		8 => '8 articles',
+		12 => '12 articles',
+		16 => '16 articles',
+		22 => '22  articles'
+	];
+
+	// 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 $licenses = [
+		'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 $ressourceType = [
+		'file' 		=> 'Fichier',
+		'url'  		=> 'URL',
+		'content'   => 'Intégrée'
+	];
+
+	public static $users = [];
+
+	/**
+	 * Mise à jour du module
+	 * Appelée par les fonctions index et config
+	 */
+	private function update() {
+		// Version 5.0
+		if (version_compare($this->getData(['module', $this->getUrl(0), 'config', 'versionData']), '1.1', '<') ) {
+			$this->setData(['module', $this->getUrl(0), 'config', 'itemsperPage', 6]);
+			$this->setData(['module', $this->getUrl(0), 'config', 'versionData','1.2']);
+		}
+	}
+
+	/**
+	 * Flux RSS
+	 */
+	public function rss() {
+		// Inclure les classes
+		include_once 'module/download/vendor/FeedWriter/Item.php';
+		include_once 'module/download/vendor/FeedWriter/Feed.php';
+		include_once 'module/download/vendor/FeedWriter/RSS2.php';
+		include_once 'module/download/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, 'thumb']));
+				$thumb = str_replace ($parts[(count($parts)-1)],'mini_' . $parts[(count($parts)-1)], $this->getData(['module', $this->getUrl(0), 'items', $itemId, 'thumb']));
+				// 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),
+					'thumb' => $this->getInput('downloadAddThumb', helper::FILTER_STRING_SHORT,true),
+					'ressourceType' => $this->getInput('downloadAddRessourceType', helper::FILTER_STRING_SHORT),
+					'file'	  => $this->getInput('downloadAddFile', helper::FILTER_STRING_SHORT),
+					'url'	  => $this->getInput('downloadAddUrl', helper::FILTER_STRING_SHORT),
+					'version' => $this->getInput('downloadAddVersion', helper::FILTER_STRING_SHORT),
+					'versionDate' => $this->getInput('downloadAddversionDate', helper::FILTER_DATETIME),
+					'license' => $this->getInput('downloadAddLicense', helper::FILTER_STRING_SHORT, true),
+					'category' => $this->getInput('downloadAddCategorie', helper::FILTER_STRING_SHORT),
+					'author' => $this->getInput('downloadAddAuthor', helper::FILTER_STRING_SHORT, true),
+					'stats' => [],
+					'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);
+		// Liste des catégories
+		/*
+		if ($this->getData(['module', $this->getUrl(0), 'categories' ]) === NULL OR
+		$this->getData(['module', $this->getUrl(0), 'categories' ]) === [] ) {
+			// Stocke une catégorie vide
+			$this->setData(['module',
+				$this->getUrl(0),
+				'categories',
+				'none',
+				'Aucune'
+			]);
+			// Alimente le formulaire
+			self::$categories = ['none' => 'Aucune'];
+		} else {
+			self::$categories = $this->getData(['module', $this->getUrl(0), 'categories' ]);
+		}
+		arsort(self::$categories);
+		*/
+		if ( $this->getData(['module', $this->getUrl(0), 'categories' ]) !== NULL ) {
+			self::$categories = $this->getData(['module', $this->getUrl(0), 'categories' ]);
+			arsort(self::$categories);
+		}
+		// 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(['module', $this->getUrl(0),'config', 'itemsperPage']));
+		// Liste des pages
+		self::$pages = $pagination['pages'];
+		// Commentaires en fonction de la pagination
+		for($i = $pagination['first']; $i < $pagination['last']; $i++) {
+			// Met en forme le tableau
+			$comment = $comments[$commentIds[$i]];
+			// Bouton d'approbation
+			$buttonApproval = '';
+			// Compatibilité avec les commentaires des versions précédentes, les valider
+			$comment['approval'] = array_key_exists('approval', $comment) === false ? true : $comment['approval'] ;
+			if ( $this->getData(['module', $this->getUrl(0),  '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() {
+		// Mise à jour des données de module
+		$this->update();
+		// 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),
+				'itemsperPage' => $this->getInput('blogConfigItemsperPage', helper::FILTER_INT,true),
+				'versionData' => $this->getData(['module', $this->getUrl(0), 'config', 'versionData'])
+				]]);
+			// 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;
+			// Filtrage des catégories selon le second élément de l'URL si valide
+			if ( $this->getUrl(2)
+				AND array_key_exists($this->getUrl(2),$this->getData(['module',$this->getUrl(0),'categories']))
+				) {
+					$filterData=[];
+					foreach ($itemIds as $key => $value) {
+						if ($this->getData(['module', $this->getUrl(0), 'items', $value,'category']) === $this->getUrl(2) ) {
+							$filterData[] = $value;
+						}
+					}
+					$itemIds = $filterData;
+			}
+			// Pagination
+			$pagination = helper::pagination($itemIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0),'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], 'versionDate'])), 'UTF-8', true)
+					? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'versionDate']))
+					: utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'versionDate'])));
+				//$heure = mb_detect_encoding(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'versionDate'])), 'UTF-8', true)
+				//	? strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'versionDate']))
+				//	: utf8_encode(strftime('%H:%M', $this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i], 'versionDate'])));
+				$stat = count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'stats']), 'time') ) === 0
+					? '0'
+					: '' .
+					count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $itemIds[$i],'stats']), 'time') )  .
+					'';
+				// Lien toutes les catégories quand le filtre est actif
+				if ($this->getUrl(2)) {
+					self::$allCategories = '(toutes)';
+				}
+				// Tableau des items
+				self::$items[] = [
+					'' .
+						$this->getData(['module', $this->getUrl(0),  'items', $itemIds[$i], 'title']) .
+						'',
+					'' .
+						$this->getData(['module', $this->getUrl(0), 'categories', $this->getData(['module', $this->getUrl(0),  'items', $itemIds[$i], 'category'])]) .
+						'',
+					$this->getData(['module', $this->getUrl(0),  'items', $itemIds[$i], 'version']),
+					//$date .' à '. $heure,
+					$date,
+					$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], [
+						'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),
+						'ressourceType' => $this->getInput('downloadEditRessourceType', helper::FILTER_STRING_SHORT),
+						'file'	  => $this->getInput('downloadEditFile', helper::FILTER_STRING_SHORT),
+						'url'	  => $this->getInput('downloadEditUrl', helper::FILTER_STRING_SHORT),
+						'thumb' => $this->getInput('downloadEditThumb', helper::FILTER_STRING_SHORT,true),
+						'version' => $this->getInput('downloadEditVersion', helper::FILTER_STRING_SHORT),
+						'versionDate' => $this->getInput('downloadEditversionDate', helper::FILTER_DATETIME),
+						'stats' => $this->getData(['module',$this->getUrl(0), 'items', $this->getUrl(2), 'stats']),
+						'license' => $this->getInput('downloadEditLicense', helper::FILTER_STRING_SHORT, true),
+						'category' => $this->getInput('downloadEditCategorie', helper::FILTER_STRING_SHORT),
+						'author' => $this->getInput('downloadEditAuthor', 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),
+						'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);
+			// Liste des catégories
+			self::$categories = $this->getData(['module', $this->getUrl(0), 'categories' ]);
+			arsort(self::$categories);
+			// 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() {
+		// Mise à jour des données de module
+		$this->update();
+		// 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
+			// Ne pas exclure la catégorie
+			AND $this->getData(['module', $this->getUrl(0), 'categories', $this->getUrl(1)]) === null
+		) {
+			// L'item ou la catégorie n'existent pas
+			if($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1)]) === null
+				 ) {
+				// Valeurs en sortie
+				$this->addOutput([
+					'access' => false
+				]);
+			}
+			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(['module', $this->getUrl(0),'config', 'itemsperPage']),'#comment'); + // Nombre de téléchargements + self::$statSum = count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items',$this->getUrl(1),'stats']), 'time') ) === 0 + ? '0' + : count(helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1),'stats']), 'time') ) ; + // 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; + } + } + // Filtrage des catégories + // Une catégorie et pas un article + if ( $this->getUrl(1) + AND array_key_exists($this->getUrl(1),$this->getData(['module',$this->getUrl(0),'categories'])) + ) { + $filterData=[]; + foreach ($itemIds as $key => $value) { + if ($this->getData(['module', $this->getUrl(0), 'items', $value,'category']) === $this->getUrl(1) ) { + $filterData[] = $value; + } + } + $itemIds = $filterData; + } + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0),'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 { + $fileName = self::FILE_DIR . 'source/' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']); + if (file_exists($fileName)) { + // Statistiques de téléchargement + $statId = helper::increment(uniqid(), $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'stats'])); + $this->setData(['module', + $this->getUrl(0), + 'items', + $this->getUrl(2), + 'stats', + $statId, [ + 'time' => time(), + 'ip' => helper::getIp() + ]]); + // Formatage http + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="'.basename($fileName).'"'); + header('Expires: 0'); + header('Cache-Control: must-revalidate'); + header('Pragma: public'); + header('Content-Length: ' . filesize($fileName)); + readfile($fileName); + exit; + } 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() { + + // Construction de la page des statistiques + $itemIds = array_keys($this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'stats'])); + // Total des téléchargements + self::$statSum = count ($itemIds); + // Pagination + $pagination = helper::pagination($itemIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0),'config', 'itemsperPage'])); + + // Liste des pages + self::$pages = $pagination['pages']; + + 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), 'stats', $itemIds[$i], 'time'])), 'UTF-8', true) + ? strftime('%d %B %Y - %H:%M',$this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'stats', $itemIds[$i], 'time'])) + : utf8_encode(strftime('%d %B %Y - %H:%M', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'stats', $itemIds[$i], 'time']))); + + // Met en forme le tableau + self::$items[] = [ + $date, + $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'stats', $itemIds[$i], 'ip']) + ]; + + } + $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([ + '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), 'stats', [] ]); + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/stats/' . $this->getUrl(2), + 'notification' => 'Purge des statistiques', + 'state' => true + ]); + } + } + + /*** + * 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] = [ + 'title' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'title']), + 'content' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'content']), + 'thumb' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'thumb']), + 'file' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'file']), + 'version' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'version']), + 'versionDate' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'versionDate']), + 'author' =>$this->getData(['module', $this->getUrl(0), 'items', $key, 'author']), + 'license' => $this->getData(['module', $this->getUrl(0), 'items', $key, 'license']), + 'category' => $this->getData(['module', $this->getUrl(0),'categories', + $this->getData(['module', $this->getUrl(0), 'items', $key, 'category']) + ]) + ]; + } + $this->addOutput([ + 'display' => self::DISPLAY_JSON, + 'content' => self::$items + ]); + } + + /** + * Gestion des catégories d'objets + */ + public function categories() { + // Soumission du formulaire + if($this->isPost()) { + // Empêche les doublons + $itemTitle = helper::increment($this->getInput('categoriesTitle', helper::FILTER_STRING_SHORT), $this->getData(['module', $this->getUrl(0), 'categories'])); + if ($itemTitle === $this->getInput('categoriesTitle', helper::FILTER_STRING_SHORT)) { + // Incrémente l'id de l'item + $itemId = helper::increment($this->getInput('categoriesTitle', helper::FILTER_ID), $this->getData(['module', $this->getUrl(0), 'categories'])); + $itemId = helper::increment($itemId, $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), + 'categories', + $itemId, + $this->getInput('categoriesTitle', helper::FILTER_STRING_SHORT, true) + ]); + $notification = 'Nouvelle catégorie créé.'; + $success = true; + } else { + $notification = 'Cette catégorie existe déjà !'; + $success = false; + } + + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/categories', + 'notification' => $notification, + 'state' => $success + ]); + } + if ($this->getData(['module',$this->getUrl(0),'categories'])) { + $categories = $this->getData(['module',$this->getUrl(0),'categories']); + // Ids des catégories par ordre alpha + $categoriesIds = array_keys($categories); + // Pagination + $pagination = helper::pagination($categoriesIds, $this->getUrl(), $this->getData(['module', $this->getUrl(0),'config', 'itemsperPage'])); + // Liste des pages + self::$pages = $pagination['pages']; + for($i = $pagination['first']; $i < $pagination['last']; $i++) { + self::$categories[ $categoriesIds[$i]] = [ + $this->getData(['module', $this->getUrl(0), 'categories', $categoriesIds[$i]]), + helper::baseUrl() . $this->getUrl(0) . '/' . $categoriesIds[$i], + template::button('categoriesEdit' . $categoriesIds[$i], [ + 'class' => 'categoriesEdit', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/categoryEdit/' . $categoriesIds[$i] . '/' . $_SESSION['csrf'] , + 'value' => template::ico('pencil') + ]), + template::button('categoriesDelete' . $categoriesIds[$i], [ + 'class' => 'categoriesDelete buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/categoryDelete/' . $categoriesIds[$i] . '/' . $_SESSION['csrf'] , + 'value' => template::ico('cancel') + ]) + ]; + } + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Catégories', + 'view' => 'categories' + ]); + } + + /** + * Edition d'une catégorie + */ + public function categoryEdit() { + // Soumission du formulaire + if($this->isPost()) { + // Id de la catégorie précédente + $oldItemId = $this->getUrl(2); + // Empêche les doublons de clé + $itemTitle = helper::increment($this->getInput('categoryEditTitle', helper::FILTER_STRING_SHORT), $this->getData(['module', $this->getUrl(0), 'categories'])); + if ($itemTitle === $this->getInput('categoryEditTitle', helper::FILTER_STRING_SHORT)) { + // Incrémente l'id de l'item + $itemId = helper::increment($this->getInput('categoryEditTitle', helper::FILTER_ID), $this->getData(['module', $this->getUrl(0), 'categories'])); + $itemId = helper::increment($itemId, $this->getData(['page'])); + $itemId = helper::increment($itemId, (array) $this->getData(['module', $this->getUrl(0)])); + $itemId = helper::increment($itemId, array_keys(self::$actions)); + // Crée la catégorie + $this->setData(['module', + $this->getUrl(0), + 'categories', + $itemId, + $this->getInput('categoryEditTitle', helper::FILTER_STRING_SHORT, true) + ]); + // Effacer la catégorie + $this->deleteData(['module', + $this->getUrl(0), + 'categories', + $this->getUrl(2) + ]); + $notification = 'La catégorie a été éditée.'; + $success = true; + + // Répercuter le changement d'Id + if ($oldItemId !== $itemId) { + $i = 0; + // Mettre à jour les catégories dans items + $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC'); + foreach ($itemIdsPublishedOns as $key => $value) { + if ($this->getData(['module', $this->getUrl(0), 'items', $key, 'category']) === $oldItemId) { + $this->setData(['module', $this->getUrl(0), 'items', $key, 'category', $itemId]); + $i++; + } + } + $notification .= ' ' . $i . ' items ont été actualisés.'; + } + } else { + $notification = 'Cette catégorie existe déjà !'; + $success = false; + } + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/categories', + 'notification' => $notification, + 'state' => $success + ]); + } + // Valeurs en sortie + $this->addOutput([ + 'title' => 'Editer une catégorie', + 'view' => 'categoryEdit' + ]); + } + + /** + * Effacement d'une catégorie + */ + public function categoryDelete() { + // La catégorie n'existe pas + if($this->getData(['module', $this->getUrl(0),'categories', $this->getUrl(2)]) === null) { + // Valeurs en sortie + $this->addOutput([ + 'access' => false + ]); + } + // Contrôle du jeton + elseif ($this->getUrl(3) !== $_SESSION['csrf']) { + // Valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/categories', + 'notification' => 'Action non autorisée' + ]); + } else { + // Mettre à jour les catégories dans items + $itemIdsPublishedOns = helper::arrayCollumn($this->getData(['module', $this->getUrl(0), 'items']), 'publishedOn', 'SORT_DESC'); + $success = true; + $i = 0; + foreach ($itemIdsPublishedOns as $key => $value) { + if ($this->getData(['module', $this->getUrl(0), 'items', $key, 'category']) === $this->getUrl(2)) { + $i++; + $success = false; + } + } + if ($success) { + // Effacer la catégorie + $this->deleteData(['module', + $this->getUrl(0), + 'categories', + $this->getUrl(2) + ]); + $notification = 'La catégorie a été supprimée'; + } else { + $notification = 'Suppression impossible, la catégorie est affectée à ' . $i . ' items.'; + } + + // valeurs en sortie + $this->addOutput([ + 'redirect' => helper::baseUrl() . $this->getUrl(0) . '/categories', + 'notification' => $notification, + 'state' => $success + ]); + } + } +} \ No newline at end of file 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..023eca02 --- /dev/null +++ b/module/download/view/add/add.php @@ -0,0 +1,183 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ true, + 'value' => 'Enregistrer en brouillon' + ]); ?> + true + ]); ?> +
+
+ 'Publier' + ]); ?> +
+
+
+
+
+

Informations sur la ressource

+
+
+ 'Titre' + ]); ?> +
+
+ 'Version' + ]); ?> +
+
+ 'Publiée le' + ]); ?> +
+
+
+
+ 'Auteur' + ]); ?> +
+
+ 'Licence' + ]); ?> +
+
+ 'Catégorie' + ]); + } else { + echo template::select('downloadAddCategorie', [''=>''], [ + 'label' => 'Pas de catégorie', + 'disabled' => true + ]); + } + ?> +
+
+ 'Capture d\'écran', + 'type' => 1 + ]); ?> +
+
+
+
+ 'Type de ressource', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'ressourceType']) + ]); ?> +
+
+
+
+ 'Fichier', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']) + ]); ?> +
+
+ 'URL', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'url']), + 'placeholder' => 'https://' + ]); ?> +
+
+
+
+
+
+
+
+ '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' => 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/categories/categories.css b/module/download/view/categories/categories.css new file mode 100644 index 00000000..38dc87fe --- /dev/null +++ b/module/download/view/categories/categories.css @@ -0,0 +1,38 @@ +/** + * 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 +*/ + + +.blockContainer { + display : none; +} + +#info .zwiico-plus-circled { + display: inline; +} + +.zwiico-minus-circled, +#info .zwiico-minus-circled { + display: none; +} + + +.zwiico-minus-circled, +.zwiico-plus-circled { + cursor: pointer; +} \ No newline at end of file diff --git a/module/download/view/categories/categories.js.php b/module/download/view/categories/categories.js.php new file mode 100644 index 00000000..c51e30ae --- /dev/null +++ b/module/download/view/categories/categories.js.php @@ -0,0 +1,70 @@ + /** + * 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/ + */ + +/** + * Confirmation de suppression + */ + $(".categoriesDelete").on("click", function() { + var _this = $(this); + return core.confirm("Êtes-vous sûr de vouloir supprimer cette catégorie ?", function() { + $(location).attr("href", _this.attr("href")); + }); +}); + +/** + * Gestion des blocs dépliants + */ + $( document).ready(function() { + + /** + * Initialisation des blocs + */ + + var i = [ "params" ]; + $.each(i,function(e) { + if (getCookie(i[e]) === "true") { + $("#" + i[e]).find(".zwiico-plus-circled").hide(); + $("#" + i[e]).find(".zwiico-minus-circled").show(); + $("#" + i[e]).find(".blockContainer").show(); + } + }); + + /** + * + * Blocs dépliants + */ + + $("div .block").click(function(e) { + $(this).find(".zwiico-plus-circled").toggle(); + $(this).find(".zwiico-minus-circled").toggle(); + $(this).find(".blockContainer").slideToggle(); + /* + * Sauvegarder la position des blocs + * true = bloc déplié + */ + document.cookie = $(this).attr('id') + "=" + $(this).find(".zwiico-minus-circled").is(":visible") + ";expires=Fri, 31 Dec 9999 23:59:59 GMT;path=/;SameSite=Lax"; + }).on("click", "span > input, input, textarea, label, option, button, a:not(.inputFile), .blockContainer", function(e) { + // Empêcher les déclenchements dans les blocs + e.stopPropagation(); + }); + +}); + +/** +* Lire un cookie s'il existe +*/ +function getCookie(name) { +var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); +return v ? v[2] : null; +} \ No newline at end of file diff --git a/module/download/view/categories/categories.php b/module/download/view/categories/categories.php new file mode 100644 index 00000000..84639d0c --- /dev/null +++ b/module/download/view/categories/categories.php @@ -0,0 +1,50 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ 'Valider' + ]); ?> +
+
+
+
+
+

Nouvelle catégorie +
+ +
+

+
+
+
+ 'Nom', + 'value' => $this->getData(['module', $this->getUrl(0), 'categories', $this->getUrl(2), 'title']) + ]); ?> +
+
+
+
+
+
+ + + + + + + +
Version n° + +
\ No newline at end of file diff --git a/module/download/view/categoryEdit/categoryEdit.css b/module/download/view/categoryEdit/categoryEdit.css new file mode 100644 index 00000000..805e9150 --- /dev/null +++ b/module/download/view/categoryEdit/categoryEdit.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/categoryEdit/categoryEdit.php b/module/download/view/categoryEdit/categoryEdit.php new file mode 100644 index 00000000..2b3932ac --- /dev/null +++ b/module/download/view/categoryEdit/categoryEdit.php @@ -0,0 +1,31 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/categories', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ 'Valider' + ]); ?> +
+
+
+
+

Editer la catégorie

+
+
+ 'Nom', + 'value' => $this->getData(['module', $this->getUrl(0), 'categories', $this->getUrl(2)]) + ]); ?> +
+
+
+
+
+ \ No newline at end of file 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..d65d4f9c --- /dev/null +++ b/module/download/view/config/config.css @@ -0,0 +1,37 @@ +/** + * 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 +*/ + +.blockContainer { + display : none; +} + +#info .zwiico-plus-circled { + display: inline; +} + +.zwiico-minus-circled, +#info .zwiico-minus-circled { + display: none; +} + + +.zwiico-minus-circled, +.zwiico-plus-circled { + cursor: pointer; +} \ 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..f2fb14f6 --- /dev/null +++ b/module/download/view/config/config.js.php @@ -0,0 +1,68 @@ +/** + * 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")); + }); +}); + +/** + * Gestion des blocs dépliants + */ +$( document).ready(function() { + + /** + * Initialisation des blocs + */ + + var i = [ "params" ]; + $.each(i,function(e) { + if (getCookie(i[e]) === "true") { + $("#" + i[e]).find(".zwiico-plus-circled").hide(); + $("#" + i[e]).find(".zwiico-minus-circled").show(); + $("#" + i[e]).find(".blockContainer").show(); + } + }); + + /** + * + * Blocs dépliants + */ + + $("div .block").click(function(e) { + $(this).find(".zwiico-plus-circled").toggle(); + $(this).find(".zwiico-minus-circled").toggle(); + $(this).find(".blockContainer").slideToggle(); + /* + * Sauvegarder la position des blocs + * true = bloc déplié + */ + document.cookie = $(this).attr('id') + "=" + $(this).find(".zwiico-minus-circled").is(":visible") + ";expires=Fri, 31 Dec 9999 23:59:59 GMT;path=/;SameSite=Lax"; + }).on("click", "span > input, input, textarea, label, option, button, a:not(.inputFile), .blockContainer", function(e) { + // Empêcher les déclenchements dans les blocs + e.stopPropagation(); + }); + + }); + +/** + * Lire un cookie s'il existe + */ + function getCookie(name) { + var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); + return v ? v[2] : null; +} \ 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..a1dd34c1 --- /dev/null +++ b/module/download/view/config/config.php @@ -0,0 +1,75 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . 'page/edit/' . $this->getUrl(0), 'items', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ +
+
+
+
+
+

Paramètres du module +
+ +
+

+
+
+
+ $this->getData(['module', $this->getUrl(0), 'config', 'feeds']), + ]); ?> +
+
+ 'Etiquette du flux', + 'value' => $this->getData(['module', $this->getUrl(0), 'config', 'feedsLabel']) + ]); ?> +
+
+ 'Articles par page', + 'selected' => $this->getData(['module', $this->getUrl(0),'config', 'itemsperPage']) + ]); ?> +
+
+
+
+
+
+ +
+
+ helper::baseUrl() . $this->getUrl(0) . '/categories', + 'value' => 'Catégories' + ]); ?> +
+
+ helper::baseUrl() . $this->getUrl(0) . '/add', + 'ico' => 'plus', + 'value' => 'Ressource' + ]); ?> +
+
+ + + + + + +
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..89e46fac --- /dev/null +++ b/module/download/view/edit/edit.js.php @@ -0,0 +1,77 @@ +/** + * 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() { + + /** Gestion des commentaires */ + + if ($("#downloadEditCloseComment").is(':checked') ) { + $(".commentOptionsWrapper").slideUp(); + } else { + $(".commentOptionsWrapper").slideDown(); + } + + if ($("#downloadEditCommentNotification").is(':checked') ) { + $("#downloadEditCommentGroupNotification").slideDown(); + } else { + $("#downloadEditCommentGroupNotification").slideUp(); + } + + + /** + * Paramétrage du sélecteur de date + * Supprimer les heures + + const datepickr = flatpickr("#downloadEditversionDate", {}); + datepickr.set (enableTime, false); + */ +}); \ 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..62315fdb --- /dev/null +++ b/module/download/view/edit/edit.php @@ -0,0 +1,193 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ true, + 'value' => 'Enregistrer en brouillon' + ]); ?> + true + ]); ?> +
+
+ 'Publier' + ]); ?> + +
+
+
+
+
+

Informations sur la ressource

+
+
+ '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), 'version']) + ]); ?> +
+
+ 'Publiée le', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'versionDate']) + ]); ?> +
+
+
+
+ 'Auteur', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'author']) + ]); ?> +
+
+ 'Licence', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'license']) + ]); ?> +
+
+ 'Catégorie', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'category']) + ]); + } else { + echo template::select('downloadEditCategorie', [''=>''], [ + 'label' => 'Pas de catégorie', + 'disabled' => true + ]); + } + ?> +
+
+ 'Capture d\'écran', + 'type' => 1, + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'thumb']) + ]); ?> +
+
+
+
+ 'Type de ressource', + 'selected' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'ressourceType']) + ]); ?> +
+
+
+
+ 'Fichier', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'file']) + ]); ?> +
+
+ 'URL', + 'value' => $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(2), 'url']), + 'placeholder' => 'https://' + ]); ?> +
+
+
+
+
+
+
+
+
+ '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..b958b637 --- /dev/null +++ b/module/download/view/index/index.css @@ -0,0 +1,69 @@ +.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; +} +h2{ + margin-bottom: 5px; + margin-top: 0px; + padding: 0px; + +} +.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..fa8b3ec7 --- /dev/null +++ b/module/download/view/index/index.php @@ -0,0 +1,67 @@ + +
+
+ $item): ?> +
+
+ makeThumb( self::FILE_DIR . 'source/' . $item['thumb'], + self::FILE_DIR . 'thumb/' . $thumb, + self::THUMBS_WIDTH); + } + + ?> + + <?php echo $item['thumb']; ?> + +
+
+ +
+
+ +
+
+ + 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..29b00c1d --- /dev/null +++ b/module/download/view/item/item.css @@ -0,0 +1,67 @@ + +#sectionTitle { + margin-top: 0; + margin-bottom: 5px; +} +.downloadItemPicture { + width: 100%; + 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; +} + +.itemInfo, .itemContent { + min-height: 25em; +} \ No newline at end of file diff --git a/module/download/view/item/item.js.php b/module/download/view/item/item.js.php new file mode 100644 index 00000000..3fbbdffb --- /dev/null +++ b/module/download/view/item/item.js.php @@ -0,0 +1,50 @@ +/** + * 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/ + */ + +/** + * Incrémente les stats + */ +$('#downloadItemFile').click(function() { + $('#downloadStats').html(function(i, val) { return val*1+1 }); +}); + +/** + * 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..eb9a42e3 --- /dev/null +++ b/module/download/view/item/item.php @@ -0,0 +1,235 @@ +
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'thumb'])): ?> +
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']) === null ? '100' : $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'pictureSize']); + $parts = explode('/',$this->getData(['module', $this->getUrl(0), 'items',$this->getUrl(1), 'thumb'])); + $thumb = str_replace ($parts[(count($parts)-1)],'mini_' . $parts[(count($parts)-1)], $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'thumb'])); + echo '' . $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'thumb']) . ''; + ?> +
+
+ + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'ressourceType']) !== 'content'): ?> +
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'ressourceType'])) { + case 'file': + $href = helper::baseUrl() . $this->getUrl(0) . '/downloadFile/' . $this->getUrl(1) . '/' . $_SESSION['csrf']; + $target = '_self'; + break; + case 'url' : + $href = $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'url']); + $target = '_blank'; + break; + } + ?> + $href, + 'value' => 'Télécharger', + 'target'=> $target + ]); + ?> +
+
+ + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'version']) ): ?> +
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'version']); ?> +
+
+ + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'date']) ): ?> +
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'date'])), 'UTF-8', true) + ? strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'date'])) + : utf8_encode(strftime('%d %B %Y', $this->getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'date']))); + echo ' du ' . $date; + ?> +
+
+ +
+
+ Auteur : + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'author']); ?> + +
+
+
+
+ Licence : + getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'license'])]; ?> + +
+
+ getData(['module', $this->getUrl(0), 'items', $this->getUrl(1), 'file'])): ?> +
+
+ Téléchargements : '?> +
+
+ +
+
+
+
+ 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'])) + : 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'] + ) + ) + ): ?> + + Editer + + + + 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','connect', 'captchaStrong']), + 'type' => $this->getData(['config','connect', 'captchaType']) + ]); ?> +
+
+ +
+
+ 'buttonGrey', + 'value' => 'Annuler' + ]); ?> +
+
+ 'Envoyer', + 'ico' => '' + ]); ?> +
+
+
+ +
+
+ $comment): ?> +
+

+ le + +

+ +
+
+
+
+ \ 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 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 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.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 new file mode 100644 index 00000000..5b778f3b --- /dev/null +++ b/module/download/view/stats/stats.php @@ -0,0 +1,36 @@ + +
+
+ 'buttonGrey', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/config', + 'ico' => 'left', + 'value' => 'Retour' + ]); ?> +
+
+ 'statsDeleteAll buttonRed', + 'href' => helper::baseUrl() . $this->getUrl(0) . '/statsDeleteAll' . '/' . $this->getUrl(2) . '/'. $_SESSION['csrf'] , + 'ico' => 'cancel', + 'value' => 'Purger' + ]); ?> +
+
+ +
+
+

Nombre de téléchargements : + +

+
+
+ + + + + + +
Version n° + +
\ No newline at end of file