Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/include/txp_image.php - 1141 lines - 38640 bytes - Summary - Text - Print

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('&#124;', array('role' => 'separator')).
 365                          sp.popTag($tagName, 'Textpattern', array('type' => 'textpattern') + $tag_url).
 366                          sp.span('&#124;', 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) : '&#160;', '', '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.' &#215; '.$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.' &#215; '.$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

title

Description

title

Description

title

title

Body