Textpattern | PHP Cross Reference | Content Management Systems |
Description: Images panel.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * https://textpattern.com/ 6 * 7 * Copyright (C) 2020 The Textpattern Development Team 8 * 9 * This file is part of Textpattern. 10 * 11 * Textpattern is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation, version 2. 14 * 15 * Textpattern is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Textpattern. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 /** 25 * Images panel. 26 * 27 * @package Admin\Image 28 */ 29 30 use Textpattern\Validator\CategoryConstraint; 31 use Textpattern\Validator\Validator; 32 use Textpattern\Search\Filter; 33 34 if (!defined('txpinterface')) { 35 die('txpinterface is undefined.'); 36 } 37 38 global $extensions; 39 $extensions = get_safe_image_types(); 40 41 include txpath.'/lib/class.thumb.php'; 42 43 if ($event == 'image') { 44 require_privs('image'); 45 46 global $all_image_cats, $all_image_authors; 47 $all_image_cats = getTree('root', 'image'); 48 $all_image_authors = the_privileged('image.edit.own', true); 49 50 $available_steps = array( 51 'image_list' => false, 52 'image_edit' => false, 53 'image_insert' => true, 54 'image_replace' => true, 55 'image_save' => true, 56 'thumbnail_insert' => true, 57 'image_change_pageby' => true, 58 'thumbnail_create' => true, 59 'thumbnail_delete' => true, 60 'image_multi_edit' => true, 61 ); 62 63 if ($step && bouncer($step, $available_steps)) { 64 $step(); 65 } else { 66 image_list(); 67 } 68 } 69 70 /** 71 * The main panel listing all images. 72 * 73 * @param string|array $message The activity message 74 */ 75 76 function image_list($message = '') 77 { 78 global $file_max_upload_size, $txp_user, $event; 79 80 pagetop(gTxt('tab_image'), $message); 81 82 extract(gpsa(array( 83 'page', 84 'sort', 85 'dir', 86 'crit', 87 'search_method', 88 ))); 89 90 if ($sort === '') { 91 $sort = get_pref('image_sort_column', 'id'); 92 } else { 93 if (!in_array($sort, array('name', 'thumbnail', 'category', 'date', 'author'))) { 94 $sort = 'id'; 95 } 96 97 set_pref('image_sort_column', $sort, 'image', PREF_HIDDEN, '', 0, PREF_PRIVATE); 98 } 99 100 if ($dir === '') { 101 $dir = get_pref('image_sort_dir', 'desc'); 102 } else { 103 $dir = ($dir == 'asc') ? "asc" : "desc"; 104 set_pref('image_sort_dir', $dir, 'image', PREF_HIDDEN, '', 0, PREF_PRIVATE); 105 } 106 107 switch ($sort) { 108 case 'name': 109 $sort_sql = "txp_image.name $dir"; 110 break; 111 case 'thumbnail': 112 $sort_sql = "txp_image.thumbnail $dir, txp_image.id ASC"; 113 break; 114 case 'category': 115 $sort_sql = "txp_category.title $dir, txp_image.id ASC"; 116 break; 117 case 'date': 118 $sort_sql = "txp_image.date $dir, txp_image.id ASC"; 119 break; 120 case 'author': 121 $sort_sql = "txp_users.RealName $dir, txp_image.id ASC"; 122 break; 123 default: 124 $sort = 'id'; 125 $sort_sql = "txp_image.id $dir"; 126 break; 127 } 128 129 $switch_dir = ($dir == 'desc') ? 'asc' : 'desc'; 130 131 $search = new Filter($event, 132 array( 133 'id' => array( 134 'column' => 'txp_image.id', 135 'label' => gTxt('id'), 136 'type' => 'integer', 137 ), 138 'name' => array( 139 'column' => 'txp_image.name', 140 'label' => gTxt('name'), 141 ), 142 'alt' => array( 143 'column' => 'txp_image.alt', 144 'label' => gTxt('alt_text'), 145 ), 146 'caption' => array( 147 'column' => 'txp_image.caption', 148 'label' => gTxt('caption'), 149 ), 150 'category' => array( 151 'column' => array('txp_image.category', 'txp_category.title'), 152 'label' => gTxt('category'), 153 ), 154 'ext' => array( 155 'column' => 'txp_image.ext', 156 'label' => gTxt('extension'), 157 ), 158 'author' => array( 159 'column' => array('txp_image.author', 'txp_users.RealName'), 160 'label' => gTxt('author'), 161 ), 162 'thumbnail' => array( 163 'column' => array('txp_image.thumbnail'), 164 'label' => gTxt('thumbnail'), 165 'type' => 'boolean', 166 ), 167 ) 168 ); 169 170 $alias_yes = '1, Yes'; 171 $alias_no = '0, No'; 172 $search->setAliases('thumbnail', array($alias_no, $alias_yes)); 173 174 list($criteria, $crit, $search_method) = $search->getFilter(array('id' => array('can_list' => true))); 175 176 $search_render_options = array('placeholder' => 'search_images'); 177 178 $sql_from = 179 safe_pfx_j('txp_image')." 180 LEFT JOIN ".safe_pfx_j('txp_category')." ON txp_category.name = txp_image.category AND txp_category.type = 'image' 181 LEFT JOIN ".safe_pfx_j('txp_users')." ON txp_users.name = txp_image.author"; 182 183 if ($crit === '') { 184 $total = getCount('txp_image', $criteria); 185 } else { 186 $total = getThing("SELECT COUNT(*) FROM $sql_from WHERE $criteria"); 187 } 188 189 $searchBlock = 190 n.tag( 191 $search->renderForm('image_list', $search_render_options), 192 'div', array( 193 'class' => 'txp-layout-4col-3span', 194 'id' => $event.'_control', 195 ) 196 ); 197 198 $createBlock = array(); 199 200 if (!is_dir(IMPATH) or !is_writeable(IMPATH)) { 201 $createBlock[] = 202 graf( 203 span(null, array('class' => 'ui-icon ui-icon-alert')).' '. 204 gTxt('img_dir_not_writeable', array('{imgdir}' => IMPATH)), 205 array('class' => 'alert-block warning') 206 ); 207 } elseif (has_privs('image.edit.own')) { 208 $categories = event_category_popup('image', '', 'image_category'); 209 $createBlock[] = 210 n.tag( 211 n.upload_form('upload_image', 'upload_image', 'image_insert[]', 'image', '', $file_max_upload_size, '', 'async', '', 212 array('postinput' => ($categories 213 ? n.tag( 214 n.tag(gTxt('category'), 'label', array('for' => 'image_category')).$categories.n, 215 'span', 216 array('class' => 'inline-file-uploader-actions')) 217 : '' 218 )), 219 'image/*' 220 ), 221 'div', array('class' => 'txp-control-panel') 222 ); 223 } 224 225 $createBlock = implode(n, $createBlock); 226 $contentBlock = ''; 227 228 $paginator = new \Textpattern\Admin\Paginator(); 229 $limit = $paginator->getLimit(); 230 231 list($page, $offset, $numPages) = pager($total, $limit, $page); 232 233 if ($total < 1) { 234 $contentBlock .= graf( 235 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 236 gTxt($crit === '' ? 'no_images_recorded' : 'no_results_found'), 237 array('class' => 'alert-block information') 238 ); 239 } else { 240 $rs = safe_query( 241 "SELECT 242 txp_image.id, 243 txp_image.name, 244 txp_image.category, 245 txp_image.ext, 246 txp_image.w, 247 txp_image.h, 248 txp_image.alt, 249 txp_image.caption, 250 UNIX_TIMESTAMP(txp_image.date) AS uDate, 251 txp_image.author, 252 txp_image.thumbnail, 253 txp_image.thumb_w, 254 txp_image.thumb_h, 255 txp_users.RealName AS realname, 256 txp_category.Title AS category_title 257 FROM $sql_from WHERE $criteria ORDER BY $sort_sql LIMIT $offset, $limit" 258 ); 259 260 $contentBlock .= pluggable_ui('image_ui', 'extend_controls', '', $rs); 261 262 if ($rs && numRows($rs)) { 263 $show_authors = !has_single_author('txp_image'); 264 265 $contentBlock .= n.tag_start('form', array( 266 'class' => 'multi_edit_form', 267 'id' => 'images_form', 268 'name' => 'longform', 269 'method' => 'post', 270 'action' => 'index.php', 271 )). 272 n.tag_start('div', array( 273 'class' => 'txp-listtables', 274 'tabindex' => 0, 275 'aria-label' => gTxt('list'), 276 )). 277 n.tag_start('table', array('class' => 'txp-list')). 278 n.tag_start('thead'). 279 tr( 280 hCell( 281 fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), 282 '', ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' 283 ). 284 column_head( 285 'ID', 'id', 'image', true, $switch_dir, $crit, $search_method, 286 (('id' == $sort) ? "$dir " : '').'txp-list-col-id' 287 ). 288 column_head( 289 'name', 'name', 'image', true, $switch_dir, $crit, $search_method, 290 (('name' == $sort) ? "$dir " : '').'txp-list-col-name' 291 ). 292 column_head( 293 'date', 'date', 'image', true, $switch_dir, $crit, $search_method, 294 (('date' == $sort) ? "$dir " : '').'txp-list-col-created date' 295 ). 296 column_head( 297 'thumbnail', 'thumbnail', 'image', true, $switch_dir, $crit, $search_method, 298 (('thumbnail' == $sort) ? "$dir " : '').'txp-list-col-thumbnail' 299 ). 300 (has_privs('tag') 301 ? hCell( 302 gTxt('tags'), '', ' class="txp-list-col-tag-build" scope="col"' 303 ) 304 : '' 305 ). 306 column_head( 307 'category', 'category', 'image', true, $switch_dir, $crit, $search_method, 308 (('category' == $sort) ? "$dir " : '').'txp-list-col-category category' 309 ). 310 ( 311 $show_authors 312 ? column_head('author', 'author', 'image', true, $switch_dir, $crit, $search_method, 313 (('author' == $sort) ? "$dir " : '').'txp-list-col-author name') 314 : '' 315 ) 316 ). 317 n.tag_end('thead'). 318 n.tag_start('tbody'); 319 320 $validator = new Validator(); 321 322 while ($a = nextRow($rs)) { 323 extract($a); 324 325 $edit_url = array( 326 'event' => 'image', 327 'step' => 'image_edit', 328 'id' => $id, 329 'sort' => $sort, 330 'dir' => $dir, 331 'page' => $page, 332 'search_method' => $search_method, 333 'crit' => $crit, 334 ); 335 336 $name = empty($name) ? 'unnamed' : txpspecialchars($name); 337 338 if ($thumbnail) { 339 if ($ext != '.swf') { 340 $thumbnail = '<img class="content-image" loading="lazy" src="'.imagesrcurl($id, $ext, true)."?$uDate".'" alt="'.$id.$ext.'" title="'.$id.$ext.'" />'; 341 $thumbexists = 1; 342 } else { 343 $thumbnail = ''; 344 $thumbexists = ''; 345 } 346 } else { 347 $thumbnail = gTxt('no'); 348 $thumbexists = ''; 349 } 350 351 if ($ext != '.swf') { 352 $tagName = 'image'; 353 $tag_url = array( 354 'id' => $id, 355 'ext' => $ext, 356 'w' => $w, 357 'h' => $h, 358 'alt' => urlencode($alt), 359 'caption' => urlencode($caption), 360 'step' => 'build', 361 ); 362 363 $tagbuilder = popTag($tagName, 'Textile', array('type' => 'textile') + $tag_url). 364 sp.span('|', array('role' => 'separator')). 365 sp.popTag($tagName, 'Textpattern', array('type' => 'textpattern') + $tag_url). 366 sp.span('|', array('role' => 'separator')). 367 sp.popTag($tagName, 'HTML', array('type' => 'html') + $tag_url); 368 } else { 369 $tagbuilder = sp; 370 } 371 372 $validator->setConstraints(array(new CategoryConstraint($category, array('type' => 'image')))); 373 $vc = $validator->validate() ? '' : ' error'; 374 375 if ($category) { 376 $category = span(txpspecialchars($category_title), array('title' => $category)); 377 } 378 379 $can_view = has_privs('image.edit.own'); 380 $can_edit = has_privs('image.edit') || ($author === $txp_user && $can_view); 381 382 $contentBlock .= tr( 383 td( 384 $can_edit ? fInput('checkbox', 'selected[]', $id) : ' ', '', 'txp-list-col-multi-edit' 385 ). 386 hCell( 387 ($can_view ? href($id, $edit_url, array('title' => gTxt('edit'))) : $id) 388 , '', array( 389 'class' => 'txp-list-col-id', 390 'scope' => 'row', 391 ) 392 ). 393 td( 394 ($can_view ? href($name, $edit_url, ' title="'.gTxt('edit').'"') : $name), '', 'txp-list-col-name txp-contain' 395 ). 396 td( 397 gTime($uDate), '', 'txp-list-col-created date' 398 ). 399 td( 400 pluggable_ui('image_ui', 'thumbnail', ($can_edit ? href($thumbnail, $edit_url) : $thumbnail), $a), '', 'txp-list-col-thumbnail'.($thumbexists ? ' has-thumbnail' : '') 401 ). 402 (has_privs('tag') 403 ? td( 404 $tagbuilder, '', 'txp-list-col-tag-build' 405 ) 406 : '' 407 ). 408 td( 409 $category, '', 'txp-list-col-category category'.$vc 410 ). 411 ( 412 $show_authors 413 ? td(span(txpspecialchars($realname), array('title' => $author)), '', 'txp-list-col-author name') 414 : '' 415 ) 416 ); 417 } 418 419 $contentBlock .= n.tag_end('tbody'). 420 n.tag_end('table'). 421 n.tag_end('div'). // End of .txp-listtables. 422 image_multiedit_form($page, $sort, $dir, $crit, $search_method). 423 tInput(). 424 n.tag_end('form'); 425 } 426 } 427 428 $pageBlock = $paginator->render(). 429 nav_form($event, $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit); 430 431 $table = new \Textpattern\Admin\Table($event); 432 echo $table->render(compact('total', 'crit'), $searchBlock, $createBlock, $contentBlock, $pageBlock). 433 n.tag( 434 null, 435 'div', array( 436 'class' => 'txp-tagbuilder-content', 437 'id' => 'tagbuild_links', 438 'aria-label' => gTxt('tagbuilder'), 439 'title' => gTxt('tagbuilder'), 440 )); 441 } 442 443 /** 444 * Renders a multi-edit form widget for images. 445 * 446 * @param int $page The page number 447 * @param string $sort The current sort value 448 * @param string $dir The current sort direction 449 * @param string $crit The current search criteria 450 * @param string $search_method The current search method 451 * @return string HTML 452 */ 453 454 function image_multiedit_form($page, $sort, $dir, $crit, $search_method) 455 { 456 global $all_image_cats, $all_image_authors; 457 458 $categories = $all_image_cats ? treeSelectInput('category', $all_image_cats, '') : ''; 459 $authors = $all_image_authors ? selectInput('author', $all_image_authors, '', true) : ''; 460 461 $methods = array( 462 'changecategory' => array( 463 'label' => gTxt('changecategory'), 464 'html' => $categories, 465 ), 466 'changeauthor' => array( 467 'label' => gTxt('changeauthor'), 468 'html' => $authors, 469 ), 470 'delete' => gTxt('delete'), 471 ); 472 473 if (!$categories) { 474 unset($methods['changecategory']); 475 } 476 477 if (has_single_author('txp_image') || !has_privs('image.edit')) { 478 unset($methods['changeauthor']); 479 } 480 481 if (!has_privs('image.delete.own') && !has_privs('image.delete')) { 482 unset($methods['delete']); 483 } 484 485 return multi_edit($methods, 'image', 'image_multi_edit', $page, $sort, $dir, $crit, $search_method); 486 } 487 488 /** 489 * Processes multi-edit actions. 490 */ 491 492 function image_multi_edit() 493 { 494 global $txp_user, $all_image_cats, $all_image_authors; 495 496 // Empty entry to permit clearing the category. 497 $categories = array(''); 498 499 foreach ($all_image_cats as $row) { 500 $categories[] = $row['name']; 501 } 502 503 $selected = ps('selected'); 504 505 if (!$selected || !is_array($selected)) { 506 return image_list(); 507 } 508 509 // Fetch and remove bogus (false) entries to prevent SQL syntax errors being thrown. 510 $selected = array_map('assert_int', $selected); 511 $selected = array_filter($selected); 512 $method = ps('edit_method'); 513 $changed = array(); 514 $key = ''; 515 516 switch ($method) { 517 case 'delete': 518 return image_delete($selected); 519 break; 520 case 'changecategory': 521 $val = ps('category'); 522 if (in_array($val, $categories)) { 523 $key = 'category'; 524 } 525 break; 526 case 'changeauthor': 527 $val = ps('author'); 528 if (has_privs('image.edit') && isset($all_image_authors[$val])) { 529 $key = 'author'; 530 } 531 break; 532 default: 533 $key = ''; 534 $val = ''; 535 break; 536 } 537 538 if (!has_privs('image.edit')) { 539 if ($selected && has_privs('image.edit.own')) { 540 $selected = safe_column("id", 'txp_image', "id IN (".join(',', $selected).") AND author = '".doSlash($txp_user)."'"); 541 } else { 542 $selected = array(); 543 } 544 } 545 546 if ($selected && $key) { 547 foreach ($selected as $id) { 548 if (safe_update('txp_image', "$key = '".doSlash($val)."'", "id = '$id'")) { 549 $changed[] = $id; 550 } 551 } 552 } 553 554 if ($changed) { 555 update_lastmod('image_updated', $changed); 556 557 return image_list(gTxt('image_updated', array('{name}' => join(', ', $changed)))); 558 } 559 560 return image_list(); 561 } 562 563 /** 564 * Renders and outputs the image editor panel. 565 * 566 * @param string|array $message The activity message 567 * @param int $id The image ID 568 */ 569 570 function image_edit($message = '', $id = '') 571 { 572 global $file_max_upload_size, $txp_user, $event, $all_image_cats; 573 574 if (!$id) { 575 $id = gps('id'); 576 } 577 578 $id = assert_int($id); 579 $rs = safe_row("*, UNIX_TIMESTAMP(date) AS uDate", 'txp_image', "id = '$id'"); 580 581 if ($rs) { 582 extract($rs); 583 584 if (!has_privs('image.edit') && !has_privs('image.edit.own')) { 585 require_privs('image.edit'); 586 587 return; 588 } 589 590 pagetop(gTxt('edit_image'), $message); 591 592 extract(gpsa(array( 593 'page', 594 'sort', 595 'dir', 596 'crit', 597 'search_method', 598 ))); 599 600 if ($ext != '.swf') { 601 $aspect = ($h == $w) ? ' square' : (($h > $w) ? ' portrait' : ' landscape'); 602 $img_info = $id.$ext.' ('.$w.' × '.$h.')'; 603 $img = '<div class="fullsize-image"><img class="content-image" src="'.imagesrcurl($id, $ext)."?$uDate".'" alt="'.$img_info.'" title="'.$img_info.'" /></div>'; 604 } else { 605 $img = $aspect = ''; 606 } 607 608 if ($thumbnail and ($ext != '.swf')) { 609 $thumb_info = $id.'t'.$ext.' ('.$thumb_w.' × '.$thumb_h.')'; 610 $thumb = '<img class="content-image" src="'.imagesrcurl($id, $ext, true)."?$uDate".'" alt="'.$thumb_info.'" title="'.$thumb_info.'" />'; 611 } else { 612 $thumb = ''; 613 614 if ($thumb_w == 0) { 615 $thumb_w = get_pref('thumb_w', 0); 616 } 617 618 if ($thumb_h == 0) { 619 $thumb_h = get_pref('thumb_h', 0); 620 } 621 } 622 623 $imageBlock = array(); 624 $thumbBlock = array(); 625 $can_edit = has_privs('image.edit') || ($author === $txp_user && has_privs('image.edit.own')); 626 $can_upload = $can_edit && is_dir(IMPATH) && is_writeable(IMPATH); 627 628 $imageBlock[] = ($can_upload 629 ? pluggable_ui( 630 'image_ui', 631 'image_edit', 632 upload_form('replace_image', 'replace_image_form', 'image_replace', 'image', $id, $file_max_upload_size, 'image-upload', ' image-replace', array('div', 'div'), '' ,'image/*'), 633 $rs) 634 : '' 635 ); 636 637 $imageBlock[] = pluggable_ui( 638 'image_ui', 639 'fullsize_image', 640 $img, 641 $rs 642 ); 643 644 $thumbBlock[] = ($can_upload 645 ? hed(gTxt('create_thumbnail').popHelp('create_thumbnail'), 3) 646 : hed(gTxt('thumbnail'), 3) 647 ); 648 649 $thumbBlock[] = ($can_upload 650 ? pluggable_ui( 651 'image_ui', 652 'thumbnail_edit', 653 upload_form('upload_thumbnail', 'upload_thumbnail', 'thumbnail_insert', 'image', $id, $file_max_upload_size, 'thumbnail-upload', ' thumbnail-upload', array('div', 'div'), '' ,'image/*'), 654 $rs) 655 : '' 656 ); 657 658 $thumbBlock[] = (check_gd($ext)) 659 ? ($can_upload 660 ? pluggable_ui( 661 'image_ui', 662 'thumbnail_create', 663 form( 664 graf( 665 n.'<label for="width">'.gTxt('width').'</label>'. 666 fInput('text', 'width', @$thumb_w, 'input-xsmall', '', '', INPUT_XSMALL, '', 'width'). 667 n.'<a class="thumbnail-swap-size">'.gTxt('swap_values').'</a>'. 668 n.'<label for="height">'.gTxt('height').'</label>'. 669 fInput('text', 'height', @$thumb_h, 'input-xsmall', '', '', INPUT_XSMALL, '', 'height'). 670 n.'<label for="crop">'.gTxt('keep_square_pixels').'</label>'. 671 checkbox('crop', 1, get_pref('thumb_crop'), '', 'crop'). 672 fInput('submit', '', gTxt('create')), ' class="edit-alter-thumbnail"' 673 ). 674 hInput('id', $id). 675 eInput('image'). 676 sInput('thumbnail_create'). 677 hInput('sort', $sort). 678 hInput('dir', $dir). 679 hInput('page', $page). 680 hInput('search_method', $search_method). 681 hInput('crit', $crit), '', '', 'post', '', '', 'thumbnail_alter_form'), 682 $rs) 683 : '' 684 ) 685 : ''; 686 687 $thumbBlock[] = pluggable_ui( 688 'image_ui', 689 'thumbnail_image', 690 '<div class="thumbnail-image">'. 691 (($thumbnail) 692 ? $thumb.n.($can_upload 693 ? dLink('image', 'thumbnail_delete', 'id', $id, '', '', '', '', array($page, $sort, $dir, $crit, $search_method)) 694 :'') 695 : gTxt('none')). 696 '</div>', 697 $rs 698 ); 699 700 echo n.'<div class="txp-layout">'. 701 n.tag( 702 hed(gTxt('edit_image'), 1, array('class' => 'txp-heading')), 703 'div', array('class' => 'txp-layout-1col') 704 ). 705 n.tag( 706 form( 707 wrapGroup( 708 'image-details', 709 inputLabel( 710 'id', 711 $id, 712 'id', '', array('class' => 'txp-form-field edit-image-id') 713 ). 714 inputLabel( 715 'image_name', 716 tag_void('input', array( 717 'id' => 'image_name', 718 'name' => 'name', 719 'size' => INPUT_REGULAR, 720 'value' => $name, 721 'type' => 'text', 722 'readonly' => !$can_edit, 723 )), 724 'image_name', '', array('class' => 'txp-form-field edit-image-name') 725 ). 726 inputLabel( 727 'image_category', 728 ($can_edit 729 ? event_category_popup('image', $category, 'image_category'). 730 n.eLink('category', 'list', '', '', gTxt('edit'), '', '', '', 'txp-option-link') 731 : (($category !== '') ? fetch_category_title($category, 'image') : gTxt('none')) 732 ), 733 'category', '', array('class' => 'txp-form-field edit-image-category') 734 ). 735 inputLabel( 736 'image_alt_text', 737 tag_void('input', array( 738 'id' => 'image_alt_text', 739 'name' => 'alt', 740 'size' => INPUT_REGULAR, 741 'value' => $alt, 742 'type' => 'text', 743 'readonly' => !$can_edit, 744 )), 745 'alt_text', '', array('class' => 'txp-form-field edit-image-alt-text') 746 ). 747 inputLabel( 748 'image_caption', 749 '<textarea id="image_caption" name="caption" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_SMALL.'"'.($can_edit ? '' : ' readonly="readonly"').'>'.htmlspecialchars($caption, ENT_NOQUOTES).'</textarea>', 750 'caption', '', array('class' => 'txp-form-field txp-form-field-textarea edit-image-caption') 751 ). 752 pluggable_ui('image_ui', 'extend_detail_form', '', $rs). 753 graf( 754 href(gTxt('go_back'), array( 755 'event' => 'image', 756 'sort' => $sort, 757 'dir' => $dir, 758 'page' => $page, 759 'search_method' => $search_method, 760 'crit' => $crit, 761 ), array('class' => 'txp-button')). 762 ($can_edit 763 ? fInput('submit', '', gTxt('save'), 'publish') 764 : '' 765 ), 766 array('class' => 'txp-edit-actions') 767 ). 768 hInput('id', $id). 769 eInput('image'). 770 sInput('image_save'). 771 hInput('sort', $sort). 772 hInput('dir', $dir). 773 hInput('page', $page). 774 hInput('search_method', $search_method). 775 hInput('crit', $crit), 776 'image_details' 777 ), 778 '', '', 'post', '', '', 'image_details_form'), 779 'div', array('class' => 'txp-layout-4col-alt') 780 ). 781 n.tag( 782 n.implode(n, $imageBlock). 783 n.'<hr />'. 784 n.tag(implode(n, $thumbBlock), 'section', array('class' => 'thumbnail-alter')), 785 'div', array('class' => 'txp-layout-4col-3span') 786 ). 787 n.'</div>'; // End of .txp-layout. 788 } else { 789 image_list(array(gTxt('unknown_image'), E_ERROR)); 790 } 791 } 792 793 /** 794 * Creates a new image from an upload. 795 */ 796 797 function image_insert() 798 { 799 if (!has_privs('image.edit.own')) { 800 require_privs('image.edit.own'); 801 802 return; 803 } 804 805 global $app_mode, $event; 806 $messages = $ids = array(); 807 $fileshandler = Txp::get('\Textpattern\Server\Files'); 808 $files = $fileshandler->refactor($_FILES['thefile']); 809 $meta = gpsa(array('caption', 'alt', 'category')); 810 811 foreach ($files as $i => $file) { 812 $chunked = $fileshandler->dechunk($file); 813 $img_result = image_data($file, $meta, 0, !$chunked); 814 @unlink($file['tmp_name']); 815 816 if (is_array($img_result)) { 817 list($message, $id) = $img_result; 818 $ids[] = $id; 819 $messages[] = array($message, 0); 820 } else { 821 $messages[] = array($img_result, E_ERROR); 822 } 823 } 824 825 if ($app_mode == 'async') { 826 $response = !empty($ids) ? 'textpattern.Relay.data.fileid = ["'.implode('","', $ids).'"].concat(textpattern.Relay.data.fileid || []);'.n : ''; 827 828 foreach ($messages as $message) { 829 $response .= 'textpattern.Console.addMessage('.json_encode((array) $message, TEXTPATTERN_JSON).', "uploadEnd");'.n; 830 } 831 832 send_script_response($response); 833 834 // Bail out. 835 return; 836 } 837 838 if (is_array($img_result)) { 839 list($message, $id) = $img_result; 840 841 return image_edit($message, $id); 842 } else { 843 return image_list(array($img_result, E_ERROR)); 844 } 845 } 846 847 /** 848 * Replaces an image with one from an upload. 849 */ 850 851 function image_replace() 852 { 853 global $txp_user; 854 855 $id = assert_int(gps('id')); 856 $rs = safe_row("*", 'txp_image', "id = '$id'"); 857 858 if (!has_privs('image.edit') && !($rs['author'] === $txp_user && has_privs('image.edit.own'))) { 859 require_privs('image.edit'); 860 861 return; 862 } 863 864 if ($rs) { 865 $meta = array( 866 'category' => $rs['category'], 867 'caption' => $rs['caption'], 868 'alt' => $rs['alt'], 869 ); 870 } else { 871 $meta = ''; 872 } 873 874 $img_result = image_data($_FILES['thefile'], $meta, $id); 875 876 if (is_array($img_result)) { 877 list($message, $id) = $img_result; 878 879 return image_edit($message, $id); 880 } else { 881 return image_edit(array($img_result, E_ERROR), $id); 882 } 883 } 884 885 /** 886 * Creates a new thumbnail from an upload. 887 */ 888 889 function thumbnail_insert() 890 { 891 global $extensions, $txp_user; 892 893 $id = assert_int(gps('id')); 894 $author = fetch('author', 'txp_image', 'id', '$id'); 895 896 if (!has_privs('image.edit') && !($author === $txp_user && has_privs('image.edit.own'))) { 897 require_privs('image.edit'); 898 899 return; 900 } 901 902 $file = $_FILES['thefile']['tmp_name']; 903 $name = $_FILES['thefile']['name']; 904 $file = get_uploaded_file($file); 905 906 if (empty($file)) { 907 image_edit(array(upload_get_errormsg(UPLOAD_ERR_NO_FILE), E_ERROR), $id); 908 909 return; 910 } 911 912 list($w, $h, $extension) = getimagesize($file); 913 914 if (($file !== false) && @$extensions[$extension]) { 915 $ext = $extensions[$extension]; 916 $newpath = IMPATH.$id.'t'.$ext; 917 918 if (shift_uploaded_file($file, $newpath) == false) { 919 image_edit(array(gTxt('directory_permissions', array('{path}' => $newpath)), E_ERROR), $id); 920 } else { 921 chmod($newpath, 0644); 922 safe_update('txp_image', "thumbnail = 1, thumb_w = $w, thumb_h = $h, date = NOW()", "id = '$id'"); 923 924 $message = gTxt('image_uploaded', array('{name}' => $name)); 925 update_lastmod('thumbnail_created', compact('id', 'w', 'h')); 926 927 image_edit($message, $id); 928 } 929 } else { 930 if ($file === false) { 931 image_edit(array(upload_get_errormsg($_FILES['thefile']['error']), E_ERROR), $id); 932 } else { 933 image_edit(array(gTxt('only_graphic_files_allowed'), E_ERROR), $id); 934 } 935 } 936 } 937 938 /** 939 * Saves image meta data. 940 */ 941 942 function image_save() 943 { 944 global $txp_user; 945 946 $varray = array_map('assert_string', gpsa(array('id', 'name', 'category', 'caption', 'alt'))); 947 extract(doSlash($varray)); 948 $id = $varray['id'] = assert_int($id); 949 $author = fetch('author', 'txp_image', 'id', $id); 950 951 if (!has_privs('image.edit') && !($author === $txp_user && has_privs('image.edit.own'))) { 952 require_privs('image.edit'); 953 954 return; 955 } 956 957 $constraints = array('category' => new CategoryConstraint(gps('category'), array('type' => 'image'))); 958 callback_event_ref('image_ui', 'validate_save', 0, $varray, $constraints); 959 $validator = new Validator($constraints); 960 961 if ($validator->validate() && safe_update( 962 'txp_image', 963 "name = '$name', 964 category = '$category', 965 alt = '$alt', 966 caption = '$caption'", 967 "id = '$id'" 968 )) { 969 $message = gTxt('image_updated', array('{name}' => doStrip($name))); 970 update_lastmod('image_saved', compact('id', 'name', 'category', 'alt', 'caption')); 971 } else { 972 $message = array(gTxt('image_save_failed'), E_ERROR); 973 } 974 975 image_list($message); 976 } 977 978 /** 979 * Deletes the given image(s) from the database and filesystem. 980 * 981 * @param array $ids List of image IDs to delete 982 */ 983 984 function image_delete($ids = array()) 985 { 986 global $txp_user, $event; 987 988 $message = ''; 989 990 // Fetch ids and remove bogus (false) entries to prevent SQL syntax errors being thrown. 991 $ids = $ids ? array_map('assert_int', $ids) : array(assert_int(ps('id'))); 992 $ids = array_filter($ids); 993 994 if (!has_privs('image.delete')) { 995 if ($ids && has_privs('image.delete.own')) { 996 $ids = safe_column("id", 'txp_image', "id IN (".join(',', $ids).") AND author = '".doSlash($txp_user)."'"); 997 } else { 998 $ids = array(); 999 } 1000 } 1001 1002 if (!empty($ids)) { 1003 $fail = array(); 1004 $rs = safe_rows_start("id, ext", 'txp_image', "id IN (".join(',', $ids).")"); 1005 1006 if ($rs) { 1007 while ($a = nextRow($rs)) { 1008 extract($a); 1009 1010 // Notify plugins of pending deletion, pass image's $id. 1011 callback_event('image_deleted', $event, false, $id); 1012 1013 $rsd = safe_delete('txp_image', "id = '$id'"); 1014 $ul = false; 1015 1016 if (is_file(IMPATH.$id.$ext)) { 1017 $ul = unlink(IMPATH.$id.$ext); 1018 } 1019 1020 if (is_file(IMPATH.$id.'t'.$ext)) { 1021 $ult = unlink(IMPATH.$id.'t'.$ext); 1022 } 1023 1024 if (!$rsd or !$ul) { 1025 $fail[] = $id; 1026 } 1027 } 1028 1029 if ($fail) { 1030 $message = array(gTxt('image_delete_failed', array('{name}' => join(', ', $fail))), E_ERROR); 1031 } else { 1032 update_lastmod('image_deleted', compact('id')); 1033 $message = gTxt('image_deleted', array('{name}' => join(', ', $ids))); 1034 } 1035 } 1036 } 1037 1038 image_list($message); 1039 } 1040 1041 /** 1042 * Saves pageby value for the image list. 1043 */ 1044 1045 function image_change_pageby() 1046 { 1047 Txp::get('\Textpattern\Admin\Paginator')->change(); 1048 image_list(); 1049 } 1050 1051 /** 1052 * Creates a new thumbnail from an existing image. 1053 */ 1054 1055 function thumbnail_create() 1056 { 1057 global $prefs, $txp_user; 1058 1059 extract(doSlash(gpsa(array('id', 'width', 'height')))); 1060 $id = assert_int($id); 1061 $author = fetch('author', 'txp_image', 'id', $id); 1062 1063 if (!has_privs('image.edit') && !($author === $txp_user && has_privs('image.edit.own'))) { 1064 require_privs('image.edit'); 1065 1066 return; 1067 } 1068 1069 $width = (int) $width; 1070 $height = (int) $height; 1071 1072 if ($width == 0) { 1073 $width = ''; 1074 } 1075 1076 if ($height == 0) { 1077 $height = ''; 1078 } 1079 1080 $crop = gps('crop'); 1081 1082 $prefs['thumb_w'] = $width; 1083 $prefs['thumb_h'] = $height; 1084 $prefs['thumb_crop'] = $crop; 1085 1086 // Hidden prefs. 1087 set_pref('thumb_w', $width, 'image', PREF_HIDDEN); 1088 set_pref('thumb_h', $height, 'image', PREF_HIDDEN); 1089 set_pref('thumb_crop', $crop, 'image', PREF_HIDDEN); 1090 1091 if ($width === '' && $height === '') { 1092 image_edit(array(gTxt('invalid_width_or_height'), E_ERROR), $id); 1093 1094 return; 1095 } 1096 1097 $t = new txp_thumb($id); 1098 $t->crop = ($crop == '1'); 1099 $t->hint = '0'; 1100 $t->width = $width; 1101 $t->height = $height; 1102 1103 if ($t->write()) { 1104 $message = gTxt('thumbnail_saved', array('{id}' => $id)); 1105 update_lastmod('thumbnail_created', compact('id', 'width', 'height', 'crop')); 1106 1107 image_edit($message, $id); 1108 } else { 1109 $message = array(gTxt('thumbnail_not_saved', array('{id}' => $id)), E_ERROR); 1110 1111 image_edit($message, $id); 1112 } 1113 } 1114 1115 /** 1116 * Delete a thumbnail. 1117 */ 1118 1119 function thumbnail_delete() 1120 { 1121 global $txp_user; 1122 1123 $id = assert_int(gps('id')); 1124 $author = fetch('author', 'txp_image', 'id', $id); 1125 1126 if (!has_privs('image.edit') && !($author === $txp_user && has_privs('image.edit.own'))) { 1127 require_privs('image.edit'); 1128 1129 return; 1130 } 1131 1132 $t = new txp_thumb($id); 1133 1134 if ($t->delete()) { 1135 callback_event('thumbnail_deleted', '', false, $id); 1136 update_lastmod('thumbnail_deleted', compact('id')); 1137 image_edit(gTxt('thumbnail_deleted'), $id); 1138 } else { 1139 image_edit(array(gTxt('thumbnail_delete_failed'), E_ERROR), $id); 1140 } 1141 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title