1<?php 2// +-----------------------------------------------------------------------+ 3// | This file is part of Piwigo. | 4// | | 5// | For copyright and license information, please view the COPYING.txt | 6// | file that was distributed with this source code. | 7// +-----------------------------------------------------------------------+ 8 9/** 10 * @package functions\html 11 */ 12 13 14/** 15 * Generates breadcrumb from categories list. 16 * Categories string returned contains categories as given in the input 17 * array $cat_informations. $cat_informations array must be an array 18 * of array( id=>?, name=>?, permalink=>?). If url input parameter is null, 19 * returns only the categories name without links. 20 * 21 * @param array $cat_informations 22 * @param string|null $url 23 * @return string 24 */ 25function get_cat_display_name($cat_informations, $url='') 26{ 27 global $conf; 28 29 //$output = '<a href="'.get_absolute_root_url().$conf['home_page'].'">'.l10n('Home').'</a>'; 30 $output = ''; 31 $is_first=true; 32 33 foreach ($cat_informations as $cat) 34 { 35 is_array($cat) or trigger_error( 36 'get_cat_display_name wrong type for category ', E_USER_WARNING 37 ); 38 39 $cat['name'] = trigger_change( 40 'render_category_name', 41 $cat['name'], 42 'get_cat_display_name' 43 ); 44 45 if ($is_first) 46 { 47 $is_first=false; 48 } 49 else 50 { 51 $output.= $conf['level_separator']; 52 } 53 54 if ( !isset($url) ) 55 { 56 $output.= $cat['name']; 57 } 58 elseif ($url == '') 59 { 60 $output.= '<a href="' 61 .make_index_url( 62 array( 63 'category' => $cat, 64 ) 65 ) 66 .'">'; 67 $output.= $cat['name'].'</a>'; 68 } 69 else 70 { 71 $output.= '<a href="'.PHPWG_ROOT_PATH.$url.$cat['id'].'">'; 72 $output.= $cat['name'].'</a>'; 73 } 74 } 75 return $output; 76} 77 78/** 79 * Generates breadcrumb from categories list using a cache. 80 * @see get_cat_display_name() 81 * 82 * @param string $uppercats 83 * @param string|null $url 84 * @param bool $single_link 85 * @param string|null $link_class 86 * @return string 87 */ 88function get_cat_display_name_cache($uppercats, 89 $url = '', 90 $single_link = false, 91 $link_class = null, 92 $auth_key=null) 93{ 94 global $cache, $conf; 95 96 $add_url_params = array(); 97 if (isset($auth_key)) 98 { 99 $add_url_params['auth'] = $auth_key; 100 } 101 102 if (!isset($cache['cat_names'])) 103 { 104 $query = ' 105SELECT id, name, permalink 106 FROM '.CATEGORIES_TABLE.' 107;'; 108 $cache['cat_names'] = query2array($query, 'id'); 109 } 110 111 $output = ''; 112 if ($single_link) 113 { 114 $single_url = add_url_params(get_root_url().$url.array_pop(explode(',', $uppercats)), $add_url_params); 115 $output.= '<a href="'.$single_url.'"'; 116 if (isset($link_class)) 117 { 118 $output.= ' class="'.$link_class.'"'; 119 } 120 $output.= '>'; 121 } 122 $is_first = true; 123 foreach (explode(',', $uppercats) as $category_id) 124 { 125 $cat = $cache['cat_names'][$category_id]; 126 127 $cat['name'] = trigger_change( 128 'render_category_name', 129 $cat['name'], 130 'get_cat_display_name_cache' 131 ); 132 133 if ($is_first) 134 { 135 $is_first = false; 136 } 137 else 138 { 139 $output.= $conf['level_separator']; 140 } 141 142 if ( !isset($url) or $single_link ) 143 { 144 $output.= $cat['name']; 145 } 146 elseif ($url == '') 147 { 148 $output.= ' 149<a href="' 150 .add_url_params( 151 make_index_url( 152 array( 153 'category' => $cat, 154 ) 155 ), 156 $add_url_params 157 ) 158 .'">'.$cat['name'].'</a>'; 159 } 160 else 161 { 162 $output.= ' 163<a href="'.PHPWG_ROOT_PATH.$url.$category_id.'">'.$cat['name'].'</a>'; 164 } 165 } 166 167 if ($single_link and isset($single_url)) 168 { 169 $output.= '</a>'; 170 } 171 172 return $output; 173} 174 175/** 176 * Generates breadcrumb for a category. 177 * @see get_cat_display_name() 178 * 179 * @param int $cat_id 180 * @param string|null $url 181 * @return string 182 */ 183function get_cat_display_name_from_id($cat_id, $url = '') 184{ 185 $cat_info = get_cat_info($cat_id); 186 return get_cat_display_name($cat_info['upper_names'], $url); 187} 188 189/** 190 * Apply basic markdown transformations to a text. 191 * newlines becomes br tags 192 * _word_ becomes underline 193 * /word/ becomes italic 194 * *word* becomes bolded 195 * urls becomes a tags 196 * 197 * @param string $content 198 * @return string 199 */ 200function render_comment_content($content) 201{ 202 $content = htmlspecialchars($content); 203 $pattern = '/(https?:\/\/\S*)/'; 204 $replacement = '<a href="$1" rel="nofollow">$1</a>'; 205 $content = preg_replace($pattern, $replacement, $content); 206 207 $content = nl2br($content); 208 209 // replace _word_ by an underlined word 210 $pattern = '/\b_(\S*)_\b/'; 211 $replacement = '<span style="text-decoration:underline;">$1</span>'; 212 $content = preg_replace($pattern, $replacement, $content); 213 214 // replace *word* by a bolded word 215 $pattern = '/\b\*(\S*)\*\b/'; 216 $replacement = '<span style="font-weight:bold;">$1</span>'; 217 $content = preg_replace($pattern, $replacement, $content); 218 219 // replace /word/ by an italic word 220 $pattern = "/\/(\S*)\/(\s)/"; 221 $replacement = '<span style="font-style:italic;">$1$2</span>'; 222 $content = preg_replace($pattern, $replacement, $content); 223 224 // TODO : add a trigger 225 226 return $content; 227} 228 229 230/** 231 * Callback used for sorting by name. 232 */ 233function name_compare($a, $b) 234{ 235 return strcmp(strtolower($a['name']), strtolower($b['name'])); 236} 237 238/** 239 * Callback used for sorting by name (slug) with cache. 240 */ 241function tag_alpha_compare($a, $b) 242{ 243 global $cache; 244 245 foreach (array($a, $b) as $tag) 246 { 247 if (!isset($cache[__FUNCTION__][ $tag['name'] ])) 248 { 249 $cache[__FUNCTION__][ $tag['name'] ] = pwg_transliterate($tag['name']); 250 } 251 } 252 253 return strcmp($cache[__FUNCTION__][ $a['name'] ], $cache[__FUNCTION__][ $b['name'] ]); 254} 255 256/** 257 * Exits the current script (or redirect to login page if not logged). 258 */ 259function access_denied() 260{ 261 global $user, $conf; 262 263 $login_url = 264 get_root_url().'identification.php?redirect=' 265 .urlencode(urlencode($_SERVER['REQUEST_URI'])); 266 267 if ( isset($user) and !is_a_guest() ) 268 { 269 set_status_header(401); 270 271 echo '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'; 272 echo '<div style="text-align:center;">'.l10n('You are not authorized to access the requested page').'<br>'; 273 echo '<a href="'.get_root_url().'identification.php">'.l10n('Identification').'</a> '; 274 echo '<a href="'.make_index_url().'">'.l10n('Home').'</a></div>'; 275 echo str_repeat( ' ', 512); //IE6 doesn't error output if below a size 276 exit(); 277 } 278 elseif (!$conf['guest_access'] and is_a_guest()) 279 { 280 redirect_http($login_url); 281 } 282 else 283 { 284 redirect_html($login_url); 285 } 286} 287 288/** 289 * Exits the current script with 403 code. 290 * @todo nice display if $template loaded 291 * 292 * @param string $msg 293 * @param string|null $alternate_url redirect to this url 294 */ 295function page_forbidden($msg, $alternate_url=null) 296{ 297 set_status_header(403); 298 if ($alternate_url==null) 299 $alternate_url = make_index_url(); 300 redirect_html( $alternate_url, 301 '<div style="text-align:left; margin-left:5em;margin-bottom:5em;"> 302<h1 style="text-align:left; font-size:36px;">'.l10n('Forbidden').'</h1><br>' 303.$msg.'</div>', 304 5 ); 305} 306 307/** 308 * Exits the current script with 400 code. 309 * @todo nice display if $template loaded 310 * 311 * @param string $msg 312 * @param string|null $alternate_url redirect to this url 313 */ 314function bad_request($msg, $alternate_url=null) 315{ 316 set_status_header(400); 317 if ($alternate_url==null) 318 $alternate_url = make_index_url(); 319 redirect_html( $alternate_url, 320 '<div style="text-align:left; margin-left:5em;margin-bottom:5em;"> 321<h1 style="text-align:left; font-size:36px;">'.l10n('Bad request').'</h1><br>' 322.$msg.'</div>', 323 5 ); 324} 325 326/** 327 * Exits the current script with 404 code. 328 * @todo nice display if $template loaded 329 * 330 * @param string $msg 331 * @param string|null $alternate_url redirect to this url 332 */ 333function page_not_found($msg, $alternate_url=null) 334{ 335 set_status_header(404); 336 if ($alternate_url==null) 337 $alternate_url = make_index_url(); 338 redirect_html( $alternate_url, 339 '<div style="text-align:left; margin-left:5em;margin-bottom:5em;"> 340<h1 style="text-align:left; font-size:36px;">'.l10n('Page not found').'</h1><br>' 341.$msg.'</div>', 342 5 ); 343} 344 345/** 346 * Exits the current script with 500 code. 347 * @todo nice display if $template loaded 348 * 349 * @param string $msg 350 * @param string|null $title 351 * @param bool $show_trace 352 */ 353function fatal_error($msg, $title=null, $show_trace=true) 354{ 355 if (empty($title)) 356 { 357 $title = l10n('Piwigo encountered a non recoverable error'); 358 } 359 360 $btrace_msg = ''; 361 if ($show_trace and function_exists('debug_backtrace')) 362 { 363 $bt = debug_backtrace(); 364 for ($i=1; $i<count($bt); $i++) 365 { 366 $class = isset($bt[$i]['class']) ? (@$bt[$i]['class'].'::') : ''; 367 $btrace_msg .= "#$i\t".$class.@$bt[$i]['function'].' '.@$bt[$i]['file']."(".@$bt[$i]['line'].")\n"; 368 } 369 $btrace_msg = trim($btrace_msg); 370 $msg .= "\n"; 371 } 372 373 $display = "<meta http-equiv='Content-Type' content='text/html; charset=utf-8'> 374<h1>$title</h1> 375<pre style='font-size:larger;background:white;color:red;padding:1em;margin:0;clear:both;display:block;width:auto;height:auto;overflow:auto'> 376<b>$msg</b> 377$btrace_msg 378</pre>\n"; 379 380 @set_status_header(500); 381 echo $display.str_repeat( ' ', 300); //IE6 doesn't error output if below a size 382 383 if ( function_exists('ini_set') ) 384 {// if possible turn off error display (we display it) 385 ini_set('display_errors', false); 386 } 387 error_reporting( E_ALL ); 388 trigger_error( strip_tags($msg).$btrace_msg, E_USER_ERROR ); 389 die(0); // just in case 390} 391 392/** 393 * Returns the breadcrumb to be displayed above thumbnails on tag page. 394 * 395 * @return string 396 */ 397function get_tags_content_title() 398{ 399 global $page; 400 $title = '<a href="'.get_root_url().'tags.php" title="'.l10n('display available tags').'">' 401 . l10n( count($page['tags']) > 1 ? 'Tags' : 'Tag' ) 402 . '</a> '; 403 404 for ($i=0; $i<count($page['tags']); $i++) 405 { 406 $title.= $i>0 ? ' + ' : ''; 407 408 $title.= 409 '<a href="' 410 .make_index_url( 411 array( 412 'tags' => array( $page['tags'][$i] ) 413 ) 414 ) 415 .'" title="' 416 .l10n('display photos linked to this tag') 417 .'">' 418 .trigger_change('render_tag_name', $page['tags'][$i]['name'], $page['tags'][$i]) 419 .'</a>'; 420 421 if (count($page['tags']) > 1) 422 { 423 $other_tags = $page['tags']; 424 unset($other_tags[$i]); 425 $remove_url = make_index_url( 426 array( 427 'tags' => $other_tags 428 ) 429 ); 430 431 $title.= 432 '<a id="TagsGroupRemoveTag" href="'.$remove_url.'" style="border:none;" title="' 433 .l10n('remove this tag from the list') 434 .'"><img src="' 435 .get_root_url().get_themeconf('icon_dir').'/remove_s.png' 436 .'" alt="x" style="vertical-align:bottom;" >' 437 .'<span class="pwg-icon pwg-icon-close" ></span>' 438 .'</a>'; 439 } 440 } 441 return $title; 442} 443 444/** 445 * Returns the breadcrumb to be displayed above thumbnails on combined categories page. 446 * 447 * @return string 448 */ 449function get_combined_categories_content_title() 450{ 451 global $page; 452 453 $title = l10n('Albums').' '; 454 455 $is_first = true; 456 $all_categories = array_merge(array($page['category']), $page['combined_categories']); 457 foreach ($all_categories as $idx => $category) 458 { 459 $title.= $is_first ? '' : ' + '; 460 $is_first = false; 461 462 $title.= get_cat_display_name(array($category)); 463 464 if (count($all_categories) > 1) // should be always the case 465 { 466 $other_cats = $all_categories; 467 unset($other_cats[$idx]); 468 469 $params = array( 470 'category' => array_shift($other_cats), 471 ); 472 473 if (count($other_cats) > 0) 474 { 475 $params['combined_categories'] = $other_cats; 476 } 477 $remove_url = make_index_url($params); 478 479 $title.= 480 '<a id="TagsGroupRemoveTag" href="'.$remove_url.'" style="border:none;" title="' 481 .l10n('remove this tag from the list') 482 .'"><img src="' 483 .get_root_url().get_themeconf('icon_dir').'/remove_s.png' 484 .'" alt="x" style="vertical-align:bottom;" >' 485 .'<span class="pwg-icon pwg-icon-close" ></span>' 486 .'</a>'; 487 } 488 } 489 490 return $title; 491} 492 493/** 494 * Sets the http status header (200,401,...) 495 * @param int $code 496 * @param string $text for exotic http codes 497 */ 498function set_status_header($code, $text='') 499{ 500 if (empty($text)) 501 { 502 switch ($code) 503 { 504 case 200: $text='OK';break; 505 case 301: $text='Moved permanently';break; 506 case 302: $text='Moved temporarily';break; 507 case 304: $text='Not modified';break; 508 case 400: $text='Bad request';break; 509 case 401: $text='Authorization required';break; 510 case 403: $text='Forbidden';break; 511 case 404: $text='Not found';break; 512 case 500: $text='Server error';break; 513 case 501: $text='Not implemented';break; 514 case 503: $text='Service unavailable';break; 515 } 516 } 517 $protocol = $_SERVER["SERVER_PROTOCOL"]; 518 if ( ('HTTP/1.1' != $protocol) && ('HTTP/1.0' != $protocol) ) 519 $protocol = 'HTTP/1.0'; 520 521 header( "$protocol $code $text", true, $code ); 522 trigger_notify('set_status_header', $code, $text); 523} 524 525/** 526 * Returns the category comment for rendering in html textual mode (subcatify) 527 * This method is called by a trigger_notify() 528 * 529 * @param string $desc 530 * @return string 531 */ 532function render_category_literal_description($desc) 533{ 534 return strip_tags($desc, '<span><p><a><br><b><i><small><big><strong><em>'); 535} 536 537/** 538 * Add known menubar blocks. 539 * This method is called by a trigger_change() 540 * 541 * @param BlockManager[] $menu_ref_arr 542 */ 543function register_default_menubar_blocks($menu_ref_arr) 544{ 545 $menu = & $menu_ref_arr[0]; 546 if ($menu->get_id() != 'menubar') 547 return; 548 $menu->register_block( new RegisteredBlock( 'mbLinks', 'Links', 'piwigo')); 549 $menu->register_block( new RegisteredBlock( 'mbCategories', 'Albums', 'piwigo')); 550 $menu->register_block( new RegisteredBlock( 'mbTags', 'Related tags', 'piwigo')); 551 $menu->register_block( new RegisteredBlock( 'mbSpecials', 'Specials', 'piwigo')); 552 $menu->register_block( new RegisteredBlock( 'mbMenu', 'Menu', 'piwigo')); 553 $menu->register_block( new RegisteredBlock( 'mbRelatedCategories', 'Related albums', 'piwigo') ); 554 555 // We hide the quick identification menu on the identification page. It 556 // would be confusing. 557 if (script_basename() != 'identification') 558 { 559 $menu->register_block( new RegisteredBlock( 'mbIdentification', 'Identification', 'piwigo') ); 560 } 561} 562 563/** 564 * Returns display name for an element. 565 * Returns 'name' if exists of name from 'file'. 566 * 567 * @param array $info at least file or name 568 * @return string 569 */ 570function render_element_name($info) 571{ 572 if (!empty($info['name'])) 573 { 574 return trigger_change('render_element_name', $info['name']); 575 } 576 return get_name_from_file($info['file']); 577} 578 579/** 580 * Returns display description for an element. 581 * 582 * @param array $info at least comment 583 * @param string $param used to identify the trigger 584 * @return string 585 */ 586function render_element_description($info, $param='') 587{ 588 if (!empty($info['comment'])) 589 { 590 return trigger_change('render_element_description', $info['comment'], $param); 591 } 592 return ''; 593} 594 595/** 596 * Add info to the title of the thumbnail based on photo properties. 597 * 598 * @param array $info hit, rating_score, nb_comments 599 * @param string $title 600 * @param string $comment 601 * @return string 602 */ 603function get_thumbnail_title($info, $title, $comment='') 604{ 605 global $conf, $user; 606 607 $details = array(); 608 609 if (!empty($info['hit'])) 610 { 611 $details[] = $info['hit'].' '.strtolower(l10n('Visits')); 612 } 613 614 if ($conf['rate'] and !empty($info['rating_score'])) 615 { 616 $details[] = strtolower(l10n('Rating score')).' '.$info['rating_score']; 617 } 618 619 if (isset($info['nb_comments']) and $info['nb_comments'] != 0) 620 { 621 $details[] = l10n_dec('%d comment', '%d comments', $info['nb_comments']); 622 } 623 624 if (count($details) > 0) 625 { 626 $title.= ' ('.implode(', ', $details).')'; 627 } 628 629 if (!empty($comment)) 630 { 631 $comment = strip_tags($comment); 632 $title.= ' '.substr($comment, 0, 100).(strlen($comment) > 100 ? '...' : ''); 633 } 634 635 $title = htmlspecialchars(strip_tags($title)); 636 $title = trigger_change('get_thumbnail_title', $title, $info); 637 638 return $title; 639} 640 641/** 642 * Event handler to protect src image urls. 643 * 644 * @param string $url 645 * @param SrcImage $src_image 646 * @return string 647 */ 648function get_src_image_url_protection_handler($url, $src_image) 649{ 650 return get_action_url($src_image->id, $src_image->is_original() ? 'e' : 'r', false); 651} 652 653/** 654 * Event handler to protect element urls. 655 * 656 * @param string $url 657 * @param array $infos id, path 658 * @return string 659 */ 660function get_element_url_protection_handler($url, $infos) 661{ 662 global $conf; 663 if ('images'==$conf['original_url_protection']) 664 {// protect only images and not other file types (for example large movies that we don't want to send through our file proxy) 665 $ext = get_extension($infos['path']); 666 if (!in_array($ext, $conf['picture_ext'])) 667 { 668 return $url; 669 } 670 } 671 return get_action_url($infos['id'], 'e', false); 672} 673 674/** 675 * Sends to the template all messages stored in $page and in the session. 676 */ 677function flush_page_messages() 678{ 679 global $template, $page; 680 if ($template->get_template_vars('page_refresh') === null) 681 { 682 foreach (array('errors','infos','warnings', 'messages') as $mode) 683 { 684 if (isset($_SESSION['page_'.$mode])) 685 { 686 $page[$mode] = array_merge($page[$mode], $_SESSION['page_'.$mode]); 687 unset($_SESSION['page_'.$mode]); 688 } 689 690 if (count($page[$mode]) != 0) 691 { 692 $template->assign($mode, $page[$mode]); 693 } 694 } 695 } 696} 697 698?> 699