1<?php 2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 3// 4// All Rights Reserved. See copyright.txt for details and a complete list of authors. 5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 6// $Id$ 7 8function wikiplugin_articles_info() 9{ 10 global $prefs; 11 return [ 12 'name' => tra('Article List'), 13 'documentation' => 'PluginArticles', 14 'description' => tra('Display multiple articles'), 15 'prefs' => [ 'feature_articles', 'wikiplugin_articles' ], 16 'iconname' => 'articles', 17 'tags' => [ 'basic' ], 18 'introduced' => 1, 19 'params' => [ 20 'usePagination' => [ 21 'required' => false, 22 'name' => tra('Use Pagination'), 23 'description' => tr('Activate pagination when the articles list is long. Default is %0', '<code>n</code>'), 24 'filter' => 'alpha', 25 'default' => 'n', 26 'since' => '1', 27 'options' => [ 28 ['text' => '', 'value' => ''], 29 ['text' => tra('Yes'), 'value' => 'y'], 30 ['text' => tra('No'), 'value' => 'n'] 31 ], 32 ], 33 'max' => [ 34 'required' => false, 35 'name' => tra('Maximum Displayed'), 36 'description' => tr('The number of articles to display in the list (use %0 to show all)', '<code>-1</code>'), 37 'filter' => 'int', 38 'since' => '1', 39 'default' => $prefs['maxRecords'], 40 ], 41 'topic' => [ 42 'required' => false, 43 'name' => tra('Topic Name Filter'), 44 'description' => tra('Filter the list of articles by topic. Example: ') . '<code>[!]topic+topic+topic</code>', 45 'filter' => 'text', 46 'since' => '1', 47 'default' => '', 48 ], 49 'topicId' => [ 50 'required' => false, 51 'name' => tra('Topic ID Filter'), 52 'description' => tra('Filter the list of articles by topic ID. Example: ') . '<code>[!]topicId+topicId+topicId</code>', 53 'filter' => 'text', 54 'accepted' => tra('Valid topic IDs'), 55 'default' => '', 56 'profile_reference' => 'article_topic', 57 'since' => '2.0', 58 ], 59 'type' => [ 60 'required' => false, 61 'name' => tra('Type Filter'), 62 'description' => tra('Filter the list of articles by types. Example: ') . '<code>[!]type+type+type</code>', 63 'filter' => 'text', 64 'since' => '1', 65 'accepted' => tra('Valid article types'), 66 'default' => '', 67 'profile_reference' => 'article_type', 68 ], 69 'categId' => [ 70 'required' => false, 71 'name' => tra('Category ID'), 72 'description' => tra('List of category IDs, separated by "%0". Only articles in all these categories are 73 listed', '<code>|</code>'), 74 'filter' => 'digits', 75 'default' => '', 76 'profile_reference' => 'category', 77 'since' => '1', 78 'separator' => '|', 79 ], 80 'lang' => [ 81 'required' => false, 82 'name' => tra('Language'), 83 'description' => tra('List only articles in this language'), 84 'filter' => 'lang', 85 'since' => '1', 86 'default' => '', 87 ], 88 'sort' => [ 89 'required' => false, 90 'name' => tra('Sort order'), 91 'description' => tr('The column and order of the sort in %0columnName_asc%1 or %0columnName_desc%1 format. 92 Defaults to %0publishDate_desc%1 (other column examples are %0title%1, %0lang%1, %0articleId%1, 93 %0authorName%1 & %0topicName%1). Use "random" to have random items.', '<code>', '</code>'), 94 'filter' => 'word', 95 'default' => 'publishDate_desc', 96 'since' => '2.0', 97 'accepted' => tra('random or column names to add _asc _desc to: ') 98 . 'created, author, title, publishDate, expireDate, articleId, topline, subtitle, lang, linkto, authorName, topicId, topicName, state, size, heading, body, isfloat, useImage, image_name, image_caption, image_type, image_size, image_x, image_y, image_data, list_image_x, list_image_y, nbreads, votes, points, type, rating, ispublished'], 99 'order' => [ 100 'required' => false, 101 'name' => tra('Specific order'), 102 'description' => tra('List of ArticleId that must appear in this order if present'), 103 'filter' => 'digits', 104 'separator' => '|', 105 'default' => '', 106 'since' => '9.0', 107 ], 108 'articleId' => [ 109 'required' => false, 110 'name' => tra('Only these articles'), 111 'description' => tr('List of article IDs to display, separated by "%0"', '<code>|</code>'), 112 'filter' => 'digits', 113 'separator' => '|', 114 'default' => '', 115 'profile_reference' => 'article', 116 'since' => '9.0', 117 ], 118 'notArticleId' => [ 119 'required' => false, 120 'name' => tra('Not these articles'), 121 'description' => tra('List of article IDs to not display, separated by "%0"', '<code>|</code>'), 122 'filter' => 'digits', 123 'separator' => '|', 124 'default' => '', 125 'profile_reference' => 'article', 126 'since' => '5.0', 127 ], 128 'quiet' => [ 129 'required' => false, 130 'name' => tra('Quiet'), 131 'description' => tra('Whether to not report when there are no articles (no reporting by default)'), 132 'filter' => 'alpha', 133 'default' => 'n', 134 'since' => '1', 135 'options' => [ 136 ['text' => '', 'value' => ''], 137 ['text' => tra('Yes'), 'value' => 'y'], 138 ['text' => tra('No'), 'value' => 'n'], 139 ], 140 ], 141 'titleonly' => [ 142 'required' => false, 143 'name' => tra('Title Only'), 144 'description' => tra('Whether to only show the title of the articles (not set to title only by default)'), 145 'filter' => 'alpha', 146 'since' => '1', 147 'default' => '', 148 'options' => [ 149 ['text' => '', 'value' => ''], 150 ['text' => tra('Yes'), 'value' => 'y'], 151 ['text' => tra('No'), 'value' => 'n'], 152 ], 153 ], 154 'fullbody' => [ 155 'required' => false, 156 'name' => tra('Show Article Body'), 157 'description' => tra('Whether to show the body of the articles instead of the heading (not set by default).'), 158 'filter' => 'alpha', 159 'default' => 'n', 160 'since' => '5', 161 'options' => [ 162 ['text' => '', 'value' => ''], 163 ['text' => tra('Yes'), 'value' => 'y'], 164 ['text' => tra('No'), 'value' => 'n'], 165 ], 166 ], 167 'start' => [ 168 'required' => false, 169 'name' => tra('Starting Article'), 170 'description' => tra('The article number that the list should start with (starts with first article by 171 default)') . '. ' . tra('This will not work if Pagination is used.'), 172 'filter' => 'int', 173 'since' => '1', 174 'default' => 0, 175 ], 176 'dateStart' => [ 177 'required' => false, 178 'name' => tra('Start Date'), 179 'description' => tra('Earliest date to select articles from.') . tr(' (%0YYYY-MM-DD%1)', '<code>', '</code>'), 180 'filter' => 'date', 181 'default' => '', 182 'since' => '5.0', 183 ], 184 'dateEnd' => [ 185 'required' => false, 186 'name' => tra('End date'), 187 'description' => tra('Latest date to select articles from.') . tr(' (%0YYYY-MM-DD%1)', '<code>', '</code>'), 188 'filter' => 'date', 189 'default' => '', 190 'since' => '5.0', 191 ], 192 'periodQuantity' => [ 193 'required' => false, 194 'name' => tra('Period quantity'), 195 'description' => tr('Numeric value to display only last articles published within a user defined 196 time-frame. Used in conjunction with the next parameter "Period unit", this parameter indicates how 197 many of those units are to be considered to define the time frame. If this parameter is set, 198 "Start Date" and "End Date" are ignored.'), 199 'filter' => 'digits', 200 'since' => '1', 201 'default' => '', 202 ], 203 'periodUnit' => [ 204 'required' => false, 205 'name' => tra('Period unit'), 206 'description' => tr('Time unit used with "Period quantity"'), 207 'filter' => 'word', 208 'since' => '1', 209 'options' => [ 210 ['text' => '', 'value' => ''], 211 ['text' => tr('Hour'), 'value' => 'hour'], 212 ['text' => tr('Day'), 'value' => 'day'], 213 ['text' => tr('Week'), 'value' => 'week'], 214 ['text' => tr('Month'), 'value' => 'month'], 215 ], 216 ], 217 'overrideDates' => [ 218 'required' => false, 219 'name' => tra('Override Dates'), 220 'description' => tra('Whether to comply with the article type\'s "show before publish" and "show after expiration" settings (not complied with by default)'), 221 'filter' => 'alpha', 222 'default' => 'n', 223 'since' => '1', 224 'options' => [ 225 ['text' => '', 'value' => ''], 226 ['text' => tra('Yes'), 'value' => 'y'], 227 ['text' => tra('No'), 'value' => 'n'], 228 ], 229 ], 230 'containerClass' => [ 231 'required' => false, 232 'name' => tra('Containing class'), 233 'description' => tr( 234 'CSS class to add to the containing "div.article" (default: "%0")', 235 '<code>wikiplugin_articles</code>' 236 ), 237 'filter' => 'text', 238 'since' => '1', 239 'accepted' => tra('Valid CSS class'), 240 'default' => 'wikiplugin_articles', 241 ], 242 'largefirstimage' => [ 243 'required' => false, 244 'name' => tra('Large First Image'), 245 'description' => tr('If set to %0 (Yes), the first image will be displayed with the dimension used to 246 view of the article', '<code>y</code>'), 247 'filter' => 'alpha', 248 'default' => 'n', 249 'since' => '6.0', 250 'options' => [ 251 ['text' => '', 'value' => ''], 252 ['text' => tra('Yes'), 'value' => 'y'], 253 ['text' => tra('No'), 'value' => 'n'], 254 ], 255 ], 256 'urlparam' => [ 257 'required' => false, 258 'name' => tra('Additional URL parameter for the link to read the article'), 259 'filter' => 'text', 260 'default' => '', 261 'since' => '6.0', 262 ], 263 'actions' => [ 264 'required' => false, 265 'name' => tra('Show actions (buttons and links)'), 266 'description' => tra('Whether to show the buttons and links to do actions on each article (for the 267 actions you have permission to do'), 268 'filter' => 'alpha', 269 'default' => 'n', 270 'since' => '6.1', 271 'options' => [ 272 ['text' => '', 'value' => ''], 273 ['text' => tra('Yes'), 'value' => 'y'], 274 ['text' => tra('No'), 'value' => 'n'], 275 ], 276 ], 277 'translationOrphan' => [ 278 'required' => false, 279 'name' => tra('No translation'), 280 'description' => tra('User- or pipe-separated list of two-letter language codes for additional languages 281 to display. List pages with no language or with a missing translation in one of the language'), 282 'filter' => 'alpha', 283 'separator' => '|', 284 'since' => '1', 285 'default' => '', 286 ], 287 'useLinktoURL' => [ 288 'required' => false, 289 'name' => tra('Use Source URL'), 290 'description' => tra('Use the external source URL as link for articles.'), 291 'filter' => 'alpha', 292 'since' => '1', 293 'default' => 'n', 294 'options' => [ 295 ['text' => '', 'value' => ''], 296 ['text' => tra('Yes'), 'value' => 'y'], 297 ['text' => tra('No'), 'value' => 'n'], 298 ], 299 ], 300 ], 301 ]; 302} 303 304function wikiplugin_articles($data, $params) 305{ 306 global $prefs, $tiki_p_read_article, $tiki_p_articles_read_heading, $pageLang; 307 $smarty = TikiLib::lib('smarty'); 308 $tikilib = TikiLib::lib('tiki'); 309 $artlib = TikiLib::lib('art'); 310 $default = ['max' => $prefs['maxRecords'], 'start' => 0, 'usePagination' => 'n', 'topicId' => '', 'topic' => '', 'sort' => 'publishDate_desc', 'type' => '', 'lang' => '', 'quiet' => 'n', 'categId' => '', 'largefirstimage' => 'n', 'urlparam' => '', 'actions' => 'n', 'translationOrphan' => '', 'headerLinks' => 'n', 'showtable' => 'n', 'useLinktoURL' => 'n']; 311 $auto_args = ['lang', 'topicId', 'topic', 'sort', 'type', 'lang', 'categId']; 312 $params = array_merge($default, $params); 313 314 extract($params, EXTR_SKIP); 315 $filter = []; 316 if ($prefs['feature_articles'] != 'y') { 317 // the feature is disabled or the user can't read articles, not even article headings 318 return(""); 319 } 320 321 $urlnext = ''; 322 if ($usePagination == 'y') { 323 //Set offset when pagniation is used 324 if (! isset($_REQUEST["offset"])) { 325 $start = 0; 326 } else { 327 $start = $_REQUEST["offset"]; 328 } 329 330 foreach ($auto_args as $arg) { 331 if (! empty($$arg)) { 332 $paramsnext[$arg] = $$arg; 333 } 334 } 335 $paramsnext['_type'] = 'absolute_path'; 336 $smarty->loadPlugin('smarty_function_query'); 337 $urlnext = smarty_function_query($paramsnext, $smarty->getEmptyInternalTemplate()); 338 } 339 340 $smarty->assign_by_ref('quiet', $quiet); 341 $smarty->assign_by_ref('urlparam', $urlparam); 342 $smarty->assign_by_ref('urlnext', $urlnext); 343 $smarty->assign_by_ref('useLinktoURL', $useLinktoURL); 344 345 if (! isset($containerClass)) { 346 $containerClass = 'wikiplugin_articles'; 347 } 348 $smarty->assign('container_class', $containerClass); 349 350 $dateStartTS = 0; 351 $dateEndTS = 0; 352 353 // if a period of time is set, date start and end are ignored 354 if (isset($periodQuantity)) { 355 switch ($periodUnit) { 356 case 'hour': 357 $periodUnit = 3600; 358 break; 359 case 'day': 360 $periodUnit = 86400; 361 break; 362 case 'week': 363 $periodUnit = 604800; 364 break; 365 case 'month': 366 $periodUnit = 2628000; 367 break; 368 default: 369 break; 370 } 371 372 if (is_int($periodUnit)) { 373 $dateStartTS = $tikilib->now - ($periodQuantity * $periodUnit); 374 $dateEndTS = $tikilib->now; 375 } 376 } else { 377 if (isset($dateStart)) { 378 $dateStartTS = strtotime($dateStart); 379 } 380 381 if (isset($dateEnd)) { 382 $dateEndTS = strtotime($dateEnd); 383 } 384 } 385 386 if (isset($fullbody) && $fullbody == 'y') { 387 $smarty->assign('fullbody', 'y'); 388 } else { 389 $smarty->assign('fullbody', 'n'); 390 $fullbody = 'n'; 391 } 392 $smarty->assign('largefirstimage', $largefirstimage); 393 if (! isset($overrideDates)) { 394 $overrideDates = 'n'; 395 } 396 397 if (! empty($translationOrphan)) { 398 $filter['translationOrphan'] = $translationOrphan; 399 } 400 if (! empty($articleId)) { 401 $filter['articleId'] = $articleId; 402 } 403 if (! empty($notArticleId)) { 404 $filter['notArticleId'] = $notArticleId; 405 } 406 407 if (! is_array($categId) || count($categId) == 0) { 408 $categIds = ''; 409 } elseif (is_array($categId) && count($categId) == 1) { 410 // For performance reasons, if there is only one value, the SQL query should not return IN () as it does with arrays 411 // So we send a single value instead of a single-value array 412 $categIds = $categId[0]; 413 } else { 414 // We want the list of articles which are in all categories 415 $categIds = [ 'AND' => $categId]; 416 } 417 418 $listpages = $artlib->list_articles($start, $max, $sort, '', $dateStartTS, $dateEndTS, 'admin', $type, $topicId, 'y', $topic, $categIds, '', '', $lang, '', '', ($overrideDates == 'y'), 'y', $filter); 419 if ($prefs['feature_multilingual'] == 'y' && empty($translationOrphan)) { 420 $multilinguallib = TikiLib::lib('multilingual'); 421 $listpages['data'] = $multilinguallib->selectLangList('article', $listpages['data'], $pageLang); 422 foreach ($listpages['data'] as &$article) { 423 $article['translations'] = $multilinguallib->getTranslations('article', $article['articleId'], $article["title"], $article['lang']); 424 } 425 } 426 427 for ($i = 0, $icount_listpages = count($listpages["data"]); $i < $icount_listpages; $i++) { 428 $listpages["data"][$i]["parsed_heading"] = TikiLib::lib('parser')->parse_data( 429 $listpages["data"][$i]["heading"], 430 [ 431 'min_one_paragraph' => true, 432 'is_html' => $artlib->is_html($listpages["data"][$i], true), 433 ] 434 ); 435 if ($fullbody == 'y') { 436 $listpages["data"][$i]["parsed_body"] = TikiLib::lib('parser')->parse_data( 437 $listpages["data"][$i]["body"], 438 [ 439 'min_one_paragraph' => true, 440 'is_html' => $artlib->is_html($listpages["data"][$i]), 441 ] 442 ); 443 } 444 $comments_prefix_var = 'article:'; 445 $comments_object_var = $listpages["data"][$i]["articleId"]; 446 $comments_objectId = $comments_prefix_var . $comments_object_var; 447 $listpages["data"][$i]["comments_cant"] = TikiLib::lib('comments')->count_comments($comments_objectId); 448 //print_r($listpages["data"][$i]['title']); 449 } 450 451 $topics = $artlib->list_topics(); 452 $smarty->assign_by_ref('topics', $topics); 453 454 if (empty($topicId)) { 455 $topicId = ''; 456 } 457 if (empty($type)) { 458 $type = ''; 459 } 460 461 if (! empty($topic) && ! strstr($topic, '!') && ! strstr($topic, '+')) { 462 $smarty->assign_by_ref('topic', $topic); 463 } elseif (! empty($topicId) && is_numeric($topicId)) { 464 $smarty->assign_by_ref('topicId', $topicId); 465 if (! empty($listpages['data'][0]['topicName'])) { 466 $smarty->assign_by_ref('topic', $listpages['data'][0]['topicName']); 467 } else { 468 $topic_info = $artlib->get_topic($topicId); 469 if (isset($topic_info['name'])) { 470 $smarty->assign_by_ref('topic', $topic_info['name']); 471 } 472 } 473 } elseif (empty($topicId)) { 474 $smarty->assign_by_ref('topicId', $topicId); 475 } 476 if (! empty($type) && ! strstr($type, '!') && ! strstr($type, '+')) { 477 $smarty->assign_by_ref('type', $type); 478 } elseif (empty($type)) { 479 $smarty->assign_by_ref('type', $type); 480 } 481 482 if ($usePagination == 'y') { 483 $smarty->assign('maxArticles', $max); 484 $smarty->assign_by_ref('offset', $start); 485 $smarty->assign_by_ref('cant', $listpages['cant']); 486 } 487 if (! empty($order)) { 488 foreach ($listpages['data'] as $i => $article) { 489 $memo[$article['articleId']] = $i; 490 } 491 foreach ($order as $articleId) { 492 if (isset($memo[$articleId])) { 493 $list[] = $listpages['data'][$memo[$articleId]]; 494 } 495 } 496 foreach ($listpages['data'] as $i => $article) { 497 if (! in_array($article['articleId'], $order)) { 498 $list[] = $article; 499 } 500 } 501 $smarty->assign_by_ref('listpages', $list); 502 } else { 503 $smarty->assign_by_ref('listpages', $listpages["data"]); 504 } 505 $smarty->assign('usePagination', $usePagination); 506 $smarty->assign_by_ref('actions', $actions); 507 $smarty->assign('headerLinks', $headerLinks); 508 509 if (isset($titleonly) && $titleonly == 'y') { 510 return "~np~ " . $smarty->fetch('tiki-view_articles-titleonly.tpl') . " ~/np~"; 511 } else { 512 return "~np~ " . $smarty->fetch('tiki-view_articles.tpl') . " ~/np~"; 513 } 514 //return str_replace("\n","",$smarty->fetch('tiki-view_articles.tpl')); // this considers the hour in the header like a link 515} 516