source: trunk/admin/include/functions.php @ 26807

Last change on this file since 26807 was 26807, checked in by mistic100, 10 years ago

feature 2999: documentation of admin/include/functions.php

  • Property svn:eol-style set to LF
File size: 58.9 KB
RevLine 
[2]1<?php
[362]2// +-----------------------------------------------------------------------+
[8728]3// | Piwigo - a PHP based photo gallery                                    |
[2297]4// +-----------------------------------------------------------------------+
[26461]5// | Copyright(C) 2008-2014 Piwigo Team                  http://piwigo.org |
[2297]6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
[2]23
[26807]24/**
25 * @package functions\admin\___
26 */
[491]27
[26807]28include_once(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
29
30
31/**
32 * Deletes a site and call delete_categories for each primary category of the site
33 *
34 * @param int $id
35 */
36function delete_site($id)
[12]37{
38  // destruction of the categories of the site
[467]39  $query = '
40SELECT id
41  FROM '.CATEGORIES_TABLE.'
42  WHERE site_id = '.$id.'
43;';
[18628]44  $category_ids = array_from_query($query, 'id');
[467]45  delete_categories($category_ids);
[1060]46
[12]47  // destruction of the site
[467]48  $query = '
49DELETE FROM '.SITES_TABLE.'
50  WHERE id = '.$id.'
51;';
[587]52  pwg_query($query);
[12]53}
[345]54
[26807]55/**
56 * Recursively deletes one or more categories.
57 * It also deletes :
58 *    - all the elements physically linked to the category (with delete_elements)
59 *    - all the links between elements and this category
60 *    - all the restrictions linked to the category
61 *
62 * @param int[] $ids
63 * @param string $photo_deletion_mode
64 *    - no_delete : delete no photo, may create orphans
65 *    - delete_orphans : delete photos that are no longer linked to any category
66 *    - force_delete : delete photos even if they are linked to another category
67 */
[8265]68function delete_categories($ids, $photo_deletion_mode='no_delete')
[12]69{
[521]70  if (count($ids) == 0)
71  {
72    return;
73  }
[657]74
75  // add sub-category ids to the given ids : if a category is deleted, all
76  // sub-categories must be so
77  $ids = get_subcat_ids($ids);
[1060]78
[8265]79  // destruction of all photos physically linked to the category
[467]80  $query = '
[1121]81SELECT id
82  FROM '.IMAGES_TABLE.'
83  WHERE storage_category_id IN (
84'.wordwrap(implode(', ', $ids), 80, "\n").')
[467]85;';
[18628]86  $element_ids = array_from_query($query, 'id');
[521]87  delete_elements($element_ids);
[21]88
[8265]89  // now, should we delete photos that are virtually linked to the category?
90  if ('delete_orphans' == $photo_deletion_mode or 'force_delete' == $photo_deletion_mode)
91  {
92    $query = '
93SELECT
94    DISTINCT(image_id)
95  FROM '.IMAGE_CATEGORY_TABLE.'
96  WHERE category_id IN ('.implode(',', $ids).')
97;';
98    $image_ids_linked = array_from_query($query, 'image_id');
[8848]99
100    if (count($image_ids_linked) > 0)
[8265]101    {
[8848]102      if ('delete_orphans' == $photo_deletion_mode)
103      {
104        $query = '
[8265]105SELECT
106    DISTINCT(image_id)
107  FROM '.IMAGE_CATEGORY_TABLE.'
108  WHERE image_id IN ('.implode(',', $image_ids_linked).')
109    AND category_id NOT IN ('.implode(',', $ids).')
110;';
[8848]111        $image_ids_not_orphans = array_from_query($query, 'image_id');
112        $image_ids_to_delete = array_diff($image_ids_linked, $image_ids_not_orphans);
113      }
[8265]114
[8848]115      if ('force_delete' == $photo_deletion_mode)
116      {
117        $image_ids_to_delete = $image_ids_linked;
118      }
119
120      delete_elements($image_ids_to_delete, true);
[8265]121    }
122  }
123
[61]124  // destruction of the links between images and this category
[467]125  $query = '
126DELETE FROM '.IMAGE_CATEGORY_TABLE.'
[657]127  WHERE category_id IN (
128'.wordwrap(implode(', ', $ids), 80, "\n").')
[467]129;';
[587]130  pwg_query($query);
[61]131
[21]132  // destruction of the access linked to the category
[467]133  $query = '
134DELETE FROM '.USER_ACCESS_TABLE.'
[657]135  WHERE cat_id IN (
136'.wordwrap(implode(', ', $ids), 80, "\n").')
[467]137;';
[587]138  pwg_query($query);
[1068]139
[467]140  $query = '
141DELETE FROM '.GROUP_ACCESS_TABLE.'
[657]142  WHERE cat_id IN (
143'.wordwrap(implode(', ', $ids), 80, "\n").')
[467]144;';
[587]145  pwg_query($query);
[21]146
[12]147  // destruction of the category
[467]148  $query = '
149DELETE FROM '.CATEGORIES_TABLE.'
[657]150  WHERE id IN (
151'.wordwrap(implode(', ', $ids), 80, "\n").')
[467]152;';
[587]153  pwg_query($query);
[498]154
[2882]155  $query='
[1866]156DELETE FROM '.OLD_PERMALINKS_TABLE.'
[1879]157  WHERE cat_id IN ('.implode(',',$ids).')';
[1866]158  pwg_query($query);
159
[2882]160  $query='
161DELETE FROM '.USER_CACHE_CATEGORIES_TABLE.'
162  WHERE cat_id IN ('.implode(',',$ids).')';
163  pwg_query($query);
164
[1605]165  trigger_action('delete_categories', $ids);
[12]166}
[345]167
[26807]168/**
169 * Deletes all files (on disk) related to given image ids.
170 *
171 * @param int[] $ids
172 * @return 0|int[] image ids where files were successfully deleted
173 */
[9191]174function delete_element_files($ids)
[12]175{
[13651]176  global $conf;
[521]177  if (count($ids) == 0)
178  {
[6873]179    return 0;
[521]180  }
[1060]181
[9191]182  $new_ids = array();
[3145]183
[9191]184  $query = '
[2678]185SELECT
186    id,
187    path,
[6873]188    representative_ext
[2678]189  FROM '.IMAGES_TABLE.'
190  WHERE id IN ('.implode(',', $ids).')
191;';
[9191]192  $result = pwg_query($query);
193  while ($row = pwg_db_fetch_assoc($result))
194  {
195    if (url_is_remote($row['path']))
[2678]196    {
[9191]197      continue;
198    }
[12917]199
[9191]200    $files = array();
201    $files[] = get_element_path($row);
[2678]202
[9191]203    if (!empty($row['representative_ext']))
204    {
[12855]205      $files[] = original_to_representative( $files[0], $row['representative_ext']);
[9191]206    }
207
208    $ok = true;
[13052]209    if (!isset($conf['never_delete_originals']))
210    {
211      foreach ($files as $path)
212      {
213        if (is_file($path) and !unlink($path))
214        {
215          $ok = false;
216          trigger_error('"'.$path.'" cannot be removed', E_USER_WARNING);
217          break;
218        }
219      }
220    }
[12917]221
[9191]222    if ($ok)
223    {
[12917]224      delete_element_derivatives($row);
[17724]225      $new_ids[] = $row['id'];
[9191]226    }
227    else
228    {
229      break;
230    }
231  }
232  return $new_ids;
233}
234
[26807]235/**
236 * Deletes elements from database.
237 * It also deletes :
238 *    - all the comments related to elements
239 *    - all the links between categories/tags and elements
240 *    - all the favorites/rates associated to elements
241 *    - removes elements from caddie
242 *
243 * @param int[] $ids
244 * @param bool $physical_deletion
245 * @return int number of deleted elements
246 */
[9191]247function delete_elements($ids, $physical_deletion=false)
248{
249  if (count($ids) == 0)
250  {
251    return 0;
252  }
253  trigger_action('begin_delete_elements', $ids);
254
255  if ($physical_deletion)
256  {
257    $ids = delete_element_files($ids);
[6873]258    if (count($ids)==0)
259    {
260      return 0;
261    }
[2678]262  }
[26807]263 
264  $ids_str = wordwrap(implode(', ', $ids), 80, "\n");
[2678]265
[12]266  // destruction of the comments on the image
[467]267  $query = '
268DELETE FROM '.COMMENTS_TABLE.'
[26807]269  WHERE image_id IN ('. $ids_str .')
[467]270;';
[587]271  pwg_query($query);
[61]272
[26807]273  // destruction of the links between images and categories
[467]274  $query = '
275DELETE FROM '.IMAGE_CATEGORY_TABLE.'
[26807]276  WHERE image_id IN ('. $ids_str .')
[467]277;';
[587]278  pwg_query($query);
[61]279
[1119]280  // destruction of the links between images and tags
281  $query = '
282DELETE FROM '.IMAGE_TAG_TABLE.'
[26807]283  WHERE image_id IN ('. $ids_str .')
[1119]284;';
285  pwg_query($query);
286
[12]287  // destruction of the favorites associated with the picture
[467]288  $query = '
289DELETE FROM '.FAVORITES_TABLE.'
[26807]290  WHERE image_id IN ('. $ids_str .')
[467]291;';
[587]292  pwg_query($query);
[523]293
294  // destruction of the rates associated to this element
295  $query = '
296DELETE FROM '.RATE_TABLE.'
[26807]297  WHERE element_id IN ('. $ids_str .')
[523]298;';
[587]299  pwg_query($query);
[764]300
[26807]301  // destruction of the caddie associated to this element
[764]302  $query = '
303DELETE FROM '.CADDIE_TABLE.'
[26807]304  WHERE element_id IN ('. $ids_str .')
[764]305;';
306  pwg_query($query);
[1060]307
[12]308  // destruction of the image
[467]309  $query = '
310DELETE FROM '.IMAGES_TABLE.'
[26807]311  WHERE id IN ('. $ids_str .')
[467]312;';
[587]313  pwg_query($query);
[491]314
[4731]315  // are the photo used as category representant?
316  $query = '
317SELECT
318    id
319  FROM '.CATEGORIES_TABLE.'
[26807]320  WHERE representative_picture_id IN ('. $ids_str .')
[4731]321;';
322  $category_ids = array_from_query($query, 'id');
323  if (count($category_ids) > 0)
324  {
325    update_category($category_ids);
326  }
[5691]327
[1605]328  trigger_action('delete_elements', $ids);
[6873]329  return count($ids);
[12]330}
[345]331
[26807]332/**
333 * Deletes an user.
334 * It also deletes all related data (accesses, favorites, permissions, etc.)
335 * @todo : accept array input
336 *
337 * @param int $user_id
338 */
[650]339function delete_user($user_id)
[12]340{
[808]341  global $conf;
[1863]342  $tables = array(
343    // destruction of the access linked to the user
344    USER_ACCESS_TABLE,
345    // destruction of data notification by mail for this user
346    USER_MAIL_NOTIFICATION_TABLE,
347    // destruction of data RSS notification for this user
348    USER_FEED_TABLE,
349    // deletion of calculated permissions linked to the user
350    USER_CACHE_TABLE,
351    // deletion of computed cache data linked to the user
352    USER_CACHE_CATEGORIES_TABLE,
353    // destruction of the group links for this user
354    USER_GROUP_TABLE,
355    // destruction of the favorites associated with the user
356    FAVORITES_TABLE,
357    // destruction of the caddie associated with the user
358    CADDIE_TABLE,
[2339]359    // deletion of piwigo specific informations
[1863]360    USER_INFOS_TABLE,
361    );
[1060]362
[1863]363  foreach ($tables as $table)
364  {
365    $query = '
366DELETE FROM '.$table.'
[650]367  WHERE user_id = '.$user_id.'
368;';
[1863]369    pwg_query($query);
370  }
[21]371
[26807]372  // purge of sessions
[650]373  $query = '
[1863]374DELETE FROM '.SESSIONS_TABLE.'
[6664]375  WHERE data LIKE \'pwg_uid|i:'.(int)$user_id.';%\'
[650]376;';
377  pwg_query($query);
[21]378
[12]379  // destruction of the user
[650]380  $query = '
381DELETE FROM '.USERS_TABLE.'
[808]382  WHERE '.$conf['user_fields']['id'].' = '.$user_id.'
[650]383;';
384  pwg_query($query);
[1605]385
386  trigger_action('delete_user', $user_id);
[12]387}
[21]388
[491]389/**
[8762]390 * Deletes all tags linked to no photo
391 */
392function delete_orphan_tags()
393{
394  $orphan_tags = get_orphan_tags();
[12917]395
[8762]396  if (count($orphan_tags) > 0)
397  {
398    $orphan_tag_ids = array();
399    foreach ($orphan_tags as $tag)
400    {
[18628]401      $orphan_tag_ids[] = $tag['id'];
[8762]402    }
403
404    $query = '
405DELETE
406  FROM '.TAGS_TABLE.'
407  WHERE id IN ('.implode(',', $orphan_tag_ids).')
408;';
409    pwg_query($query);
410  }
411}
412
413/**
414 * Get all tags (id + name) linked to no photo
415 */
416function get_orphan_tags()
417{
418  $query = '
419SELECT
420    id,
421    name
422  FROM '.TAGS_TABLE.'
423    LEFT JOIN '.IMAGE_TAG_TABLE.' ON id = tag_id
424  WHERE tag_id IS NULL
425;';
[18628]426  return array_from_query($query);
[8762]427}
428
429/**
[2324]430 * Verifies that the representative picture really exists in the db and
[26807]431 * picks up a random representative if possible and based on config.
[491]432 *
[26807]433 * @param 'all'|int|int[] $ids
[491]434 */
[2324]435function update_category($ids = 'all')
[61]436{
[809]437  global $conf;
[1060]438
[2324]439  if ($ids=='all')
[61]440  {
[2324]441    $where_cats = '1=1';
[61]442  }
[2324]443  elseif ( !is_array($ids) )
[635]444  {
[2324]445    $where_cats = '%s='.$ids;
[635]446  }
[2324]447  else
[61]448  {
[2324]449    if (count($ids) == 0)
450    {
451      return false;
452    }
453    $where_cats = '%s IN('.wordwrap(implode(', ', $ids), 120, "\n").')';
[491]454  }
[2333]455
[2324]456  // find all categories where the setted representative is not possible :
457  // the picture does not exist
458  $query = '
459SELECT DISTINCT c.id
[809]460  FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i
461    ON c.representative_picture_id = i.id
[491]462  WHERE representative_picture_id IS NOT NULL
[2324]463    AND '.sprintf($where_cats, 'c.id').'
[809]464    AND i.id IS NULL
[491]465;';
[2324]466  $wrong_representant = array_from_query($query, 'id');
[809]467
[2324]468  if (count($wrong_representant) > 0)
469  {
470    $query = '
[809]471UPDATE '.CATEGORIES_TABLE.'
472  SET representative_picture_id = NULL
[2324]473  WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 120, "\n").')
[809]474;';
[2324]475    pwg_query($query);
476  }
[809]477
[2324]478  if (!$conf['allow_random_representative'])
479  {
480    // If the random representant is not allowed, we need to find
481    // categories with elements and with no representant. Those categories
482    // must be added to the list of categories to set to a random
483    // representant.
484    $query = '
485SELECT DISTINCT id
486  FROM '.CATEGORIES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.'
487    ON id = category_id
[635]488  WHERE representative_picture_id IS NULL
[2324]489    AND '.sprintf($where_cats, 'category_id').'
[491]490;';
[2324]491    $to_rand = array_from_query($query, 'id');
492    if (count($to_rand) > 0)
493    {
494      set_random_representant($to_rand);
[133]495    }
[61]496  }
497}
498
[825]499/**
[26807]500 * Checks and repairs IMAGE_CATEGORY_TABLE integrity.
501 * Removes all entries from the table which correspond to a deleted image.
[20544]502 */
503function images_integrity()
504{
505  $query = '
506SELECT
507    image_id
508  FROM '.IMAGE_CATEGORY_TABLE.'
509    LEFT JOIN '.IMAGES_TABLE.' ON id = image_id
510  WHERE id IS NULL
511;';
512  $result = pwg_query($query);
513  $orphan_image_ids = array_from_query($query, 'image_id');
514
515  if (count($orphan_image_ids) > 0)
516  {
517    $query = '
518DELETE
519  FROM '.IMAGE_CATEGORY_TABLE.'
520  WHERE image_id IN ('.implode(',', $orphan_image_ids).')
521;';
522    pwg_query($query);
523  }
524}
525
526/**
[26807]527 * Returns an array containing sub-directories which are potentially
528 * a category.
529 * Directories named ".svn", "thumbnail", "pwg_high" or "pwg_representative"
530 * are omitted.
[657]531 *
[26807]532 * @param string $basedir (eg: ./galleries)
533 * @return string[]
[657]534 */
535function get_fs_directories($path, $recursive = true)
536{
537  $dirs = array();
[26807]538  $path = rtrim($path, '/');
[1060]539
[657]540  if (is_dir($path))
541  {
542    if ($contents = opendir($path))
543    {
544      while (($node = readdir($contents)) !== false)
545      {
[12642]546        if ($node != '.'
[657]547            and $node != '..'
[1006]548            and $node != '.svn'
[657]549            and $node != 'thumbnail'
550            and $node != 'pwg_high'
[12642]551            and $node != 'pwg_representative'
552            and is_dir($path.'/'.$node))
[657]553        {
[25018]554          $dirs[] = $path.'/'.$node;
[657]555          if ($recursive)
556          {
557            $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node));
558          }
559        }
560      }
[2478]561      closedir($contents);
[657]562    }
563  }
564
565  return $dirs;
566}
567
[606]568/**
[26807]569 * Orders categories (update categories.rank and global_rank database fields)
570 * so that rank field are consecutive integers starting at 1 for each child.
[625]571 */
[2306]572function update_global_rank()
[625]573{
574  $query = '
[4385]575SELECT id, id_uppercat, uppercats, rank, global_rank
[625]576  FROM '.CATEGORIES_TABLE.'
[2491]577  ORDER BY id_uppercat,rank,name';
[625]578
[2306]579  $cat_map = array();
[625]580
[2306]581  $current_rank = 0;
582  $current_uppercat = '';
583
[625]584  $result = pwg_query($query);
[4325]585  while ($row = pwg_db_fetch_assoc($result))
[625]586  {
[2306]587    if ($row['id_uppercat'] != $current_uppercat)
588    {
589      $current_rank = 0;
590      $current_uppercat = $row['id_uppercat'];
591    }
592    ++$current_rank;
593    $cat =
594      array(
595        'rank' =>        $current_rank,
596        'rank_changed' =>$current_rank!=$row['rank'],
597        'global_rank' => $row['global_rank'],
598        'uppercats' =>   $row['uppercats'],
599        );
600    $cat_map[ $row['id'] ] = $cat;
[625]601  }
[1060]602
[625]603  $datas = array();
[2306]604
605  foreach( $cat_map as $id=>$cat )
[625]606  {
[2306]607    $new_global_rank = preg_replace(
[1082]608          '/(\d+)/e',
[2306]609          "\$cat_map['$1']['rank']",
610          str_replace(',', '.', $cat['uppercats'] )
611          );
612    if ( $cat['rank_changed']
613      or $new_global_rank!=$cat['global_rank']
614      )
615    {
616      $datas[] = array(
617          'id' => $id,
618          'rank' => $cat['rank'],
619          'global_rank' => $new_global_rank,
620        );
621    }
[625]622  }
623
[1082]624  mass_updates(
625    CATEGORIES_TABLE,
626    array(
627      'primary' => array('id'),
[2306]628      'update'  => array('rank', 'global_rank')
[1082]629      ),
630    $datas
631    );
[2306]632  return count($datas);
[625]633}
[632]634
635/**
[26807]636 * Change the **visible** property on a set of categories.
[632]637 *
[26807]638 * @param int[] $categories
639 * @param boolean|string $value
[632]640 */
641function set_cat_visible($categories, $value)
642{
[26807]643  if ( ($value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) === null )
[632]644  {
[2491]645    trigger_error("set_cat_visible invalid param $value", E_USER_WARNING);
[632]646    return false;
647  }
648
649  // unlocking a category => all its parent categories become unlocked
[26807]650  if ($value)
[632]651  {
[657]652    $uppercats = get_uppercat_ids($categories);
[632]653    $query = '
654UPDATE '.CATEGORIES_TABLE.'
655  SET visible = \'true\'
[2491]656  WHERE id IN ('.implode(',', $uppercats).')';
[632]657    pwg_query($query);
658  }
659  // locking a category   => all its child categories become locked
[26807]660  else
[632]661  {
662    $subcats = get_subcat_ids($categories);
663    $query = '
664UPDATE '.CATEGORIES_TABLE.'
665  SET visible = \'false\'
[2491]666  WHERE id IN ('.implode(',', $subcats).')';
[632]667    pwg_query($query);
668  }
669}
670
671/**
[26807]672 * Change the **status** property on a set of categories : private or public.
[632]673 *
[26807]674 * @param int[] $categories
675 * @param string $value
[632]676 */
677function set_cat_status($categories, $value)
678{
679  if (!in_array($value, array('public', 'private')))
680  {
[2491]681    trigger_error("set_cat_status invalid param $value", E_USER_WARNING);
[632]682    return false;
683  }
684
685  // make public a category => all its parent categories become public
686  if ($value == 'public')
687  {
[657]688    $uppercats = get_uppercat_ids($categories);
[632]689    $query = '
690UPDATE '.CATEGORIES_TABLE.'
691  SET status = \'public\'
692  WHERE id IN ('.implode(',', $uppercats).')
693;';
694    pwg_query($query);
695  }
696  // make a category private => all its child categories become private
697  if ($value == 'private')
698  {
699    $subcats = get_subcat_ids($categories);
700    $query = '
701UPDATE '.CATEGORIES_TABLE.'
702  SET status = \'private\'
[2491]703  WHERE id IN ('.implode(',', $subcats).')';
[632]704    pwg_query($query);
705  }
706}
[633]707
708/**
[26807]709 * Returns all uppercats category ids of the given category ids.
[657]710 *
[26807]711 * @param int[] $cat_ids
712 * @return int[]
[657]713 */
714function get_uppercat_ids($cat_ids)
715{
716  if (!is_array($cat_ids) or count($cat_ids) < 1)
717  {
718    return array();
719  }
[1060]720
[657]721  $uppercats = array();
722
723  $query = '
724SELECT uppercats
725  FROM '.CATEGORIES_TABLE.'
726  WHERE id IN ('.implode(',', $cat_ids).')
727;';
728  $result = pwg_query($query);
[4325]729  while ($row = pwg_db_fetch_assoc($result))
[657]730  {
731    $uppercats = array_merge($uppercats,
732                             explode(',', $row['uppercats']));
733  }
734  $uppercats = array_unique($uppercats);
735
736  return $uppercats;
737}
738
739/**
[26807]740 * Set a new random representant to the categories.
[633]741 *
[26807]742 * @param int[] $categories
[633]743 */
744function set_random_representant($categories)
745{
746  $datas = array();
747  foreach ($categories as $category_id)
748  {
749    $query = '
750SELECT image_id
751  FROM '.IMAGE_CATEGORY_TABLE.'
752  WHERE category_id = '.$category_id.'
[4331]753  ORDER BY '.DB_RANDOM_FUNCTION.'()
[4334]754  LIMIT 1
[633]755;';
[4325]756    list($representative) = pwg_db_fetch_row(pwg_query($query));
[1428]757
[25018]758    $datas[] = array(
759      'id' => $category_id,
760      'representative_picture_id' => $representative,
[1428]761      );
[633]762  }
763
[1428]764  mass_updates(
765    CATEGORIES_TABLE,
766    array(
767      'primary' => array('id'),
768      'update' => array('representative_picture_id')
769      ),
770    $datas
771    );
[633]772}
[638]773
774/**
[26807]775 * Returns the fulldir for each given category id.
[657]776 *
[26807]777 * @param int[] intcat_ids
778 * @return string[]
[657]779 */
780function get_fulldirs($cat_ids)
781{
782  if (count($cat_ids) == 0)
783  {
784    return array();
785  }
[1060]786
[657]787  // caching directories of existing categories
788  $query = '
789SELECT id, dir
790  FROM '.CATEGORIES_TABLE.'
791  WHERE dir IS NOT NULL
792;';
[2560]793  $cat_dirs = simple_hash_from_query($query, 'id', 'dir');
[657]794
[672]795  // caching galleries_url
796  $query = '
797SELECT id, galleries_url
798  FROM '.SITES_TABLE.'
799;';
[2560]800  $galleries_url = simple_hash_from_query($query, 'id', 'galleries_url');
[672]801
802  // categories : id, site_id, uppercats
[657]803  $query = '
[672]804SELECT id, uppercats, site_id
[657]805  FROM '.CATEGORIES_TABLE.'
[2560]806  WHERE dir IS NOT NULL
807    AND id IN (
[657]808'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
809;';
[18628]810  $categories = array_from_query($query);
[1060]811
[657]812  // filling $cat_fulldirs
813  $cat_fulldirs = array();
[672]814  foreach ($categories as $category)
[657]815  {
[672]816    $uppercats = str_replace(',', '/', $category['uppercats']);
817    $cat_fulldirs[$category['id']] = $galleries_url[$category['site_id']];
818    $cat_fulldirs[$category['id']].= preg_replace('/(\d+)/e',
819                                                  "\$cat_dirs['$1']",
820                                                  $uppercats);
[657]821  }
822
823  return $cat_fulldirs;
824}
825
826/**
[26807]827 * Returns an array with all file system files according to $conf['file_ext']
[657]828 *
829 * @param string $path
[26807]830 * @param bool $recursive
[657]831 * @return array
832 */
833function get_fs($path, $recursive = true)
834{
835  global $conf;
836
837  // because isset is faster than in_array...
838  if (!isset($conf['flip_picture_ext']))
839  {
840    $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
841  }
842  if (!isset($conf['flip_file_ext']))
843  {
844    $conf['flip_file_ext'] = array_flip($conf['file_ext']);
845  }
846
847  $fs['elements'] = array();
848  $fs['thumbnails'] = array();
849  $fs['representatives'] = array();
850  $subdirs = array();
851
852  if (is_dir($path))
853  {
854    if ($contents = opendir($path))
855    {
856      while (($node = readdir($contents)) !== false)
857      {
[12640]858        if ($node == '.' or $node == '..') continue;
859
[657]860        if (is_file($path.'/'.$node))
861        {
862          $extension = get_extension($node);
[1060]863
[657]864//          if (in_array($extension, $conf['picture_ext']))
865          if (isset($conf['flip_picture_ext'][$extension]))
866          {
867            if (basename($path) == 'thumbnail')
868            {
[25018]869              $fs['thumbnails'][] = $path.'/'.$node;
[657]870            }
871            else if (basename($path) == 'pwg_representative')
872            {
[25018]873              $fs['representatives'][] = $path.'/'.$node;
[657]874            }
875            else
876            {
[25018]877              $fs['elements'][] = $path.'/'.$node;
[657]878            }
879          }
880//          else if (in_array($extension, $conf['file_ext']))
881          else if (isset($conf['flip_file_ext'][$extension]))
882          {
[25018]883            $fs['elements'][] = $path.'/'.$node;
[657]884          }
885        }
[12640]886        else if (is_dir($path.'/'.$node) and $node != 'pwg_high' and $recursive)
[657]887        {
[25018]888          $subdirs[] = $node;
[657]889        }
890      }
891    }
892    closedir($contents);
893
894    foreach ($subdirs as $subdir)
895    {
896      $tmp_fs = get_fs($path.'/'.$subdir);
897
898      $fs['elements']        = array_merge($fs['elements'],
899                                           $tmp_fs['elements']);
[1060]900
[657]901      $fs['thumbnails']      = array_merge($fs['thumbnails'],
902                                           $tmp_fs['thumbnails']);
[1060]903
[657]904      $fs['representatives'] = array_merge($fs['representatives'],
905                                           $tmp_fs['representatives']);
906    }
907  }
908  return $fs;
909}
[672]910
911/**
[26807]912 * Synchronize base users list and related users list.
[809]913 *
[26807]914 * Compares and synchronizes base users table (USERS_TABLE) with its child
[809]915 * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each
916 * base user must be present in child tables, users in child tables not
917 * present in base table must be deleted.
[808]918 */
919function sync_users()
920{
921  global $conf;
[1060]922
[808]923  $query = '
924SELECT '.$conf['user_fields']['id'].' AS id
925  FROM '.USERS_TABLE.'
926;';
927  $base_users = array_from_query($query, 'id');
928
929  $query = '
930SELECT user_id
931  FROM '.USER_INFOS_TABLE.'
932;';
933  $infos_users = array_from_query($query, 'user_id');
934
935  // users present in $base_users and not in $infos_users must be added
936  $to_create = array_diff($base_users, $infos_users);
937
938  if (count($to_create) > 0)
939  {
[1926]940    create_user_infos($to_create);
[808]941  }
942
[809]943  // users present in user related tables must be present in the base user
944  // table
[1082]945  $tables = array(
946    USER_MAIL_NOTIFICATION_TABLE,
947    USER_FEED_TABLE,
948    USER_INFOS_TABLE,
949    USER_ACCESS_TABLE,
950    USER_CACHE_TABLE,
[1624]951    USER_CACHE_CATEGORIES_TABLE,
[1082]952    USER_GROUP_TABLE
953    );
[1592]954
[809]955  foreach ($tables as $table)
[808]956  {
957    $query = '
[2095]958SELECT DISTINCT user_id
[809]959  FROM '.$table.'
960;';
[1082]961    $to_delete = array_diff(
962      array_from_query($query, 'user_id'),
963      $base_users
964      );
[1060]965
[809]966    if (count($to_delete) > 0)
967    {
968      $query = '
[808]969DELETE
[809]970  FROM '.$table.'
[808]971  WHERE user_id in ('.implode(',', $to_delete).')
972;';
[809]973      pwg_query($query);
974    }
975  }
976}
977
978/**
[26807]979 * Updates categories.uppercats field based on categories.id + categories.id_uppercat
[809]980 */
981function update_uppercats()
982{
983  $query = '
[2304]984SELECT id, id_uppercat, uppercats
[809]985  FROM '.CATEGORIES_TABLE.'
986;';
[2304]987  $cat_map = hash_from_query($query, 'id');
[1060]988
[2304]989  $datas = array();
990  foreach ($cat_map as $id => $cat)
[809]991  {
[2304]992    $upper_list = array();
[809]993
994    $uppercat = $id;
[2304]995    while ($uppercat)
996    {
[25018]997      $upper_list[] = $uppercat;
[2304]998      $uppercat = $cat_map[$uppercat]['id_uppercat'];
999    }
[809]1000
[2304]1001    $new_uppercats = implode(',', array_reverse($upper_list));
1002    if ($new_uppercats != $cat['uppercats'])
[809]1003    {
[25018]1004      $datas[] = array(
1005        'id' => $id,
1006        'uppercats' => $new_uppercats
[2304]1007        );
[809]1008    }
1009  }
1010  $fields = array('primary' => array('id'), 'update' => array('uppercats'));
1011  mass_updates(CATEGORIES_TABLE, $fields, $datas);
1012}
1013
1014/**
[26807]1015 * Update images.path field base on images.file and storage categories fulldirs.
[809]1016 */
1017function update_path()
1018{
1019  $query = '
[1121]1020SELECT DISTINCT(storage_category_id)
1021  FROM '.IMAGES_TABLE.'
[2575]1022  WHERE storage_category_id IS NOT NULL
[809]1023;';
[1121]1024  $cat_ids = array_from_query($query, 'storage_category_id');
1025  $fulldirs = get_fulldirs($cat_ids);
[1060]1026
[1121]1027  foreach ($cat_ids as $cat_id)
[809]1028  {
1029    $query = '
1030UPDATE '.IMAGES_TABLE.'
[5691]1031  SET path = '.pwg_db_concat(array("'".$fulldirs[$cat_id]."/'",'file')).'
[1121]1032  WHERE storage_category_id = '.$cat_id.'
[809]1033;';
[808]1034    pwg_query($query);
[809]1035  }
[808]1036}
[809]1037
1038/**
[26807]1039 * Change the parent category of the given categories. The categories are
[809]1040 * supposed virtual.
1041 *
[26807]1042 * @param int[] $category_ids
1043 * @param int $new_parent (-1 for root)
[809]1044 */
[881]1045function move_categories($category_ids, $new_parent = -1)
[809]1046{
[881]1047  global $page;
1048
1049  if (count($category_ids) == 0)
1050  {
1051    return;
1052  }
1053
1054  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
1055
1056  $categories = array();
[1060]1057
[809]1058  $query = '
[881]1059SELECT id, id_uppercat, status, uppercats
[809]1060  FROM '.CATEGORIES_TABLE.'
[881]1061  WHERE id IN ('.implode(',', $category_ids).')
[809]1062;';
[881]1063  $result = pwg_query($query);
[4325]1064  while ($row = pwg_db_fetch_assoc($result))
[809]1065  {
[881]1066    $categories[$row['id']] =
1067      array(
1068        'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
1069        'status' => $row['status'],
1070        'uppercats' => $row['uppercats']
1071        );
[809]1072  }
[1060]1073
[881]1074  // is the movement possible? The movement is impossible if you try to move
1075  // a category in a sub-category or itself
1076  if ('NULL' != $new_parent)
1077  {
1078    $query = '
1079SELECT uppercats
1080  FROM '.CATEGORIES_TABLE.'
1081  WHERE id = '.$new_parent.'
1082;';
[4325]1083    list($new_parent_uppercats) = pwg_db_fetch_row(pwg_query($query));
[881]1084
1085    foreach ($categories as $category)
1086    {
1087      // technically, you can't move a category with uppercats 12,125,13,14
1088      // into a new parent category with uppercats 12,125,13,14,24
[8967]1089      if (preg_match('/^'.$category['uppercats'].'(,|$)/', $new_parent_uppercats))
[881]1090      {
[25018]1091        $page['errors'][] = l10n('You cannot move an album in its own sub album');
[881]1092        return;
1093      }
1094    }
1095  }
[1060]1096
[26807]1097  $tables = array(
1098    USER_ACCESS_TABLE => 'user_id',
1099    GROUP_ACCESS_TABLE => 'group_id'
1100    );
[1060]1101
[809]1102  $query = '
1103UPDATE '.CATEGORIES_TABLE.'
1104  SET id_uppercat = '.$new_parent.'
[881]1105  WHERE id IN ('.implode(',', $category_ids).')
[809]1106;';
1107  pwg_query($query);
1108
1109  update_uppercats();
1110  update_global_rank();
1111
1112  // status and related permissions management
1113  if ('NULL' == $new_parent)
1114  {
1115    $parent_status = 'public';
1116  }
1117  else
1118  {
1119    $query = '
1120SELECT status
1121  FROM '.CATEGORIES_TABLE.'
1122  WHERE id = '.$new_parent.'
1123;';
[4325]1124    list($parent_status) = pwg_db_fetch_row(pwg_query($query));
[809]1125  }
1126
1127  if ('private' == $parent_status)
1128  {
[881]1129    foreach ($categories as $cat_id => $category)
[809]1130    {
[24987]1131      if ('public' == $category['status'])
[809]1132      {
[24987]1133        set_cat_status(array($cat_id), 'private');
1134      }
1135     
1136      $subcats = get_subcat_ids(array($cat_id));
[1060]1137
[24987]1138      foreach ($tables as $table => $field)
1139      {
1140        $query = '
[809]1141SELECT '.$field.'
1142  FROM '.$table.'
[881]1143  WHERE cat_id = '.$cat_id.'
[809]1144;';
[24987]1145        $category_access = array_from_query($query, $field);
[809]1146
[24987]1147        $query = '
[809]1148SELECT '.$field.'
1149  FROM '.$table.'
[881]1150  WHERE cat_id = '.$new_parent.'
[809]1151;';
[24987]1152        $parent_access = array_from_query($query, $field);
[1060]1153
[24987]1154        $to_delete = array_diff($category_access, $parent_access);
[1060]1155
[24987]1156        if (count($to_delete) > 0)
1157        {
1158          $query = '
[809]1159DELETE FROM '.$table.'
1160  WHERE '.$field.' IN ('.implode(',', $to_delete).')
[881]1161    AND cat_id IN ('.implode(',', $subcats).')
[809]1162;';
[24987]1163          pwg_query($query);
[809]1164        }
1165      }
1166    }
1167  }
[881]1168
[25018]1169  $page['infos'][] = l10n_dec(
1170    '%d album moved', '%d albums moved',
1171    count($categories)
[881]1172    );
[809]1173}
[1064]1174
1175/**
[26807]1176 * Create a virtual category.
[1064]1177 *
[26807]1178 * @param string $category_name
1179 * @param int $parent_id
1180 * @param array $options
1181 *    - boolean commentable
1182 *    - boolean visible
1183 *    - string status
1184 *    - string comment
1185 *    - boolean inherit
1186 * @return array ('info', 'id') or ('error')
[1064]1187 */
[17669]1188function create_virtual_category($category_name, $parent_id=null, $options=array())
[1064]1189{
[11728]1190  global $conf, $user;
[1068]1191
[1064]1192  // is the given category name only containing blank spaces ?
1193  if (preg_match('/^\s*$/', $category_name))
1194  {
[6988]1195    return array('error' => l10n('The name of an album must not be empty'));
[1064]1196  }
[17765]1197
[1064]1198  $insert = array(
1199    'name' => $category_name,
[12763]1200    'rank' => 0,
[17669]1201    'global_rank' => 0,
[1064]1202    );
[1068]1203
[17669]1204  // is the album commentable?
1205  if (isset($options['commentable']) and is_bool($options['commentable']))
[1064]1206  {
[17669]1207    $insert['commentable'] = $options['commentable'];
1208  }
1209  else
1210  {
1211    $insert['commentable'] = $conf['newcat_default_commentable'];
1212  }
1213  $insert['commentable'] = boolean_to_string($insert['commentable']);
1214
1215  // is the album temporarily locked? (only visible by administrators,
1216  // whatever permissions) (may be overwritten if parent album is not
1217  // visible)
1218  if (isset($options['visible']) and is_bool($options['visible']))
1219  {
1220    $insert['visible'] = $options['visible'];
1221  }
1222  else
1223  {
1224    $insert['visible'] = $conf['newcat_default_visible'];
1225  }
1226  $insert['visible'] = boolean_to_string($insert['visible']);
1227
1228  // is the album private? (may be overwritten if parent album is private)
1229  if (isset($options['status']) and 'private' == $options['status'])
1230  {
1231    $insert['status'] = 'private';
1232  }
1233  else
1234  {
1235    $insert['status'] = $conf['newcat_default_status'];
1236  }
1237
1238  // any description for this album?
1239  if (isset($options['comment']))
1240  {
[21070]1241    $insert['comment'] = $conf['allow_html_descriptions'] ? $options['comment'] : strip_tags($options['comment']);
[17669]1242  }
1243
1244  if (!empty($parent_id) and is_numeric($parent_id))
1245  {
[1064]1246    $query = '
1247SELECT id, uppercats, global_rank, visible, status
1248  FROM '.CATEGORIES_TABLE.'
1249  WHERE id = '.$parent_id.'
1250;';
[4325]1251    $parent = pwg_db_fetch_assoc(pwg_query($query));
[1064]1252
[4265]1253    $insert['id_uppercat'] = $parent['id'];
1254    $insert['global_rank'] = $parent['global_rank'].'.'.$insert['rank'];
[1068]1255
[1064]1256    // at creation, must a category be visible or not ? Warning : if the
1257    // parent category is invisible, the category is automatically create
1258    // invisible. (invisible = locked)
1259    if ('false' == $parent['visible'])
1260    {
[4265]1261      $insert['visible'] = 'false';
[1064]1262    }
[1068]1263
[1064]1264    // at creation, must a category be public or private ? Warning : if the
1265    // parent category is private, the category is automatically create
1266    // private.
1267    if ('private' == $parent['status'])
1268    {
[4265]1269      $insert['status'] = 'private';
[1064]1270    }
[17669]1271
1272    $uppercats_prefix = $parent['uppercats'].',';
[1064]1273  }
1274  else
1275  {
[17669]1276    $uppercats_prefix = '';
[1064]1277  }
1278
1279  // we have then to add the virtual category
[17669]1280  single_insert(CATEGORIES_TABLE, $insert);
1281  $inserted_id = pwg_db_insert_id(CATEGORIES_TABLE);
1282
1283  single_update(
[1064]1284    CATEGORIES_TABLE,
[17669]1285    array('uppercats' => $uppercats_prefix.$inserted_id),
1286    array('id' => $inserted_id)
[1064]1287    );
1288
[12763]1289  update_global_rank();
1290
[23886]1291  if ('private' == $insert['status'] and !empty($insert['id_uppercat']) and ((isset($options['inherit']) and $options['inherit']) or $conf['inheritance_by_default']) )
[11728]1292  {
[23272]1293    $query = '
1294      SELECT group_id
1295      FROM '.GROUP_ACCESS_TABLE.'
1296      WHERE cat_id = '.$insert['id_uppercat'].'
1297    ;';
1298    $granted_grps =  array_from_query($query, 'group_id');
1299    $inserts = array();
1300    foreach ($granted_grps as $granted_grp)
1301    {
[25018]1302      $inserts[] = array(
1303        'group_id' => $granted_grp,
1304        'cat_id' => $inserted_id
[23272]1305        );
1306    }
1307    mass_inserts(GROUP_ACCESS_TABLE, array('group_id','cat_id'), $inserts);
1308
1309    $query = '
1310      SELECT user_id
1311      FROM '.USER_ACCESS_TABLE.'
1312      WHERE cat_id = '.$insert['id_uppercat'].'
1313    ;';
1314    $granted_users =  array_from_query($query, 'user_id');
1315    add_permission_on_category($inserted_id, array_unique(array_merge(get_admins(), array($user['id']), $granted_users)));
1316  }
1317  else if ('private' == $insert['status'])
1318  {
[11728]1319    add_permission_on_category($inserted_id, array_unique(array_merge(get_admins(), array($user['id']))));
1320  }
1321
[1064]1322  return array(
[6969]1323    'info' => l10n('Virtual album added'),
[1064]1324    'id'   => $inserted_id,
1325    );
1326}
[1111]1327
1328/**
[26807]1329 * Set tags to an image.
1330 * Warning: given tags are all tags associated to the image, not additionnal tags.
[1119]1331 *
[26807]1332 * @param int[] $tags
1333 * @param int $image_id
[1119]1334 */
1335function set_tags($tags, $image_id)
1336{
[17724]1337  set_tags_of( array($image_id=>$tags) );
[1119]1338}
1339
1340/**
1341 * Add new tags to a set of images.
1342 *
[26807]1343 * @param int[] $tags
1344 * @param int[] $images
[1119]1345 */
1346function add_tags($tags, $images)
1347{
[5691]1348  if (count($tags) == 0 or count($images) == 0)
[1119]1349  {
1350    return;
1351  }
[1592]1352
[1119]1353  // we can't insert twice the same {image_id,tag_id} so we must first
1354  // delete lines we'll insert later
1355  $query = '
1356DELETE
1357  FROM '.IMAGE_TAG_TABLE.'
1358  WHERE image_id IN ('.implode(',', $images).')
1359    AND tag_id IN ('.implode(',', $tags).')
1360;';
1361  pwg_query($query);
1362
1363  $inserts = array();
1364  foreach ($images as $image_id)
1365  {
[17724]1366    foreach ( array_unique($tags) as $tag_id)
[1119]1367    {
[17724]1368      $inserts[] = array(
[1119]1369          'image_id' => $image_id,
1370          'tag_id' => $tag_id,
1371        );
1372    }
1373  }
1374  mass_inserts(
1375    IMAGE_TAG_TABLE,
1376    array_keys($inserts[0]),
1377    $inserts
1378    );
[21817]1379  invalidate_user_cache_nb_tags();
[1119]1380}
1381
[12032]1382/**
[26807]1383 * Delete tags and tags associations.
[12032]1384 *
[26807]1385 * @param int[] $tag_ids
[12032]1386 */
1387function delete_tags($tag_ids)
1388{
1389  if (is_numeric($tag_ids))
1390  {
1391    $tag_ids = array($tag_ids);
1392  }
1393
1394  if (!is_array($tag_ids))
1395  {
1396    return false;
1397  }
[12917]1398
[12032]1399  $query = '
1400DELETE
1401  FROM '.IMAGE_TAG_TABLE.'
1402  WHERE tag_id IN ('.implode(',', $tag_ids).')
1403;';
1404  pwg_query($query);
[12917]1405
[12032]1406  $query = '
1407DELETE
1408  FROM '.TAGS_TABLE.'
1409  WHERE id IN ('.implode(',', $tag_ids).')
1410;';
1411  pwg_query($query);
[21817]1412
1413  invalidate_user_cache_nb_tags();
[12032]1414}
1415
[26807]1416/**
1417 * Returns a tag id from its name. If nothing found, create a new tag.
1418 *
1419 * @param string $tag_name
1420 * @return int
1421 */
1422function tag_id_from_tag_name($tag_name, $create=true)
[1119]1423{
1424  global $page;
[2478]1425
[2350]1426  $tag_name = trim($tag_name);
[1119]1427  if (isset($page['tag_id_from_tag_name_cache'][$tag_name]))
1428  {
1429    return $page['tag_id_from_tag_name_cache'][$tag_name];
1430  }
[1592]1431
[17724]1432  // search existing by exact name
[1119]1433  $query = '
1434SELECT id
1435  FROM '.TAGS_TABLE.'
1436  WHERE name = \''.$tag_name.'\'
1437;';
[17724]1438  if (count($existing_tags = array_from_query($query, 'id')) == 0)
[1119]1439  {
[17724]1440    // search existing by case insensitive name
1441    $query = '
1442SELECT id
1443  FROM '.TAGS_TABLE.'
1444  WHERE CONVERT(name, CHAR) = \''.$tag_name.'\'
1445;';
1446    if (count($existing_tags = array_from_query($query, 'id')) == 0)
1447    {
1448      $url_name = trigger_event('render_tag_url', $tag_name);
1449      // search existing by url name
1450      $query = '
1451SELECT id
1452  FROM '.TAGS_TABLE.'
1453  WHERE url_name = \''.$url_name.'\'
1454;';
1455      if (count($existing_tags = array_from_query($query, 'id')) == 0)
1456      {
1457        mass_inserts(
1458          TAGS_TABLE,
1459          array('name', 'url_name'),
1460          array(
1461            array(
1462              'name' => $tag_name,
1463              'url_name' => $url_name,
1464              )
1465            )
1466          );
[26807]1467
1468        invalidate_user_cache_nb_tags();
1469
[17724]1470        $page['tag_id_from_tag_name_cache'][$tag_name] = pwg_db_insert_id(TAGS_TABLE);
1471        return $page['tag_id_from_tag_name_cache'][$tag_name];
1472      }
1473    }
[1119]1474  }
1475
[17724]1476  $page['tag_id_from_tag_name_cache'][$tag_name] = $existing_tags[0];
[1119]1477  return $page['tag_id_from_tag_name_cache'][$tag_name];
1478}
1479
[26807]1480/**
1481 * Set tags of images. Overwrites all existing associations.
1482 *
1483 * @param array $tags_of - keys are image ids, values are array of tag ids
1484 */
[1119]1485function set_tags_of($tags_of)
1486{
1487  if (count($tags_of) > 0)
1488  {
1489    $query = '
1490DELETE
1491  FROM '.IMAGE_TAG_TABLE.'
1492  WHERE image_id IN ('.implode(',', array_keys($tags_of)).')
1493;';
1494    pwg_query($query);
1495
1496    $inserts = array();
[1592]1497
[1119]1498    foreach ($tags_of as $image_id => $tag_ids)
1499    {
[17724]1500      foreach (array_unique($tag_ids) as $tag_id)
[1119]1501      {
[17724]1502        $inserts[] = array(
[1119]1503            'image_id' => $image_id,
1504            'tag_id' => $tag_id,
1505          );
1506      }
1507    }
1508
[17765]1509    if (count($inserts))
1510    {
1511      mass_inserts(
1512        IMAGE_TAG_TABLE,
1513        array_keys($inserts[0]),
1514        $inserts
1515        );
1516    }
[21817]1517
1518    invalidate_user_cache_nb_tags();
[1119]1519  }
1520}
1521
1522/**
[1121]1523 * Associate a list of images to a list of categories.
[26807]1524 * The function will not duplicate links and will preserve ranks.
[1121]1525 *
[26807]1526 * @param int[] $images
1527 * @param int[] $categories
[1121]1528 */
1529function associate_images_to_categories($images, $categories)
1530{
1531  if (count($images) == 0
1532      or count($categories) == 0)
1533  {
1534    return false;
1535  }
[17765]1536
[17424]1537  // get existing associations
[1121]1538  $query = '
[17424]1539SELECT
1540    image_id,
1541    category_id
[1121]1542  FROM '.IMAGE_CATEGORY_TABLE.'
1543  WHERE image_id IN ('.implode(',', $images).')
1544    AND category_id IN ('.implode(',', $categories).')
1545;';
[17424]1546  $result = pwg_query($query);
[17765]1547
[17424]1548  $existing = array();
1549  while ($row = pwg_db_fetch_assoc($result))
1550  {
1551    $existing[ $row['category_id'] ][] = $row['image_id'];
1552  }
[1121]1553
[17424]1554  // get max rank of each categories
[17002]1555  $query = '
1556SELECT
1557    category_id,
1558    MAX(rank) AS max_rank
1559  FROM '.IMAGE_CATEGORY_TABLE.'
1560  WHERE rank IS NOT NULL
1561    AND category_id IN ('.implode(',', $categories).')
1562  GROUP BY category_id
1563;';
1564
1565  $current_rank_of = simple_hash_from_query(
1566    $query,
1567    'category_id',
1568    'max_rank'
1569    );
1570
[17424]1571  // associate only not already associated images
[1121]1572  $inserts = array();
1573  foreach ($categories as $category_id)
1574  {
[17002]1575    if (!isset($current_rank_of[$category_id]))
1576    {
1577      $current_rank_of[$category_id] = 0;
1578    }
[17424]1579    if (!isset($existing[$category_id]))
1580    {
1581      $existing[$category_id] = array();
1582    }
[17002]1583
[1121]1584    foreach ($images as $image_id)
1585    {
[17424]1586      if (!in_array($image_id, $existing[$category_id]))
1587      {
1588        $rank = ++$current_rank_of[$category_id];
[17002]1589
[25018]1590        $inserts[] = array(
1591          'image_id' => $image_id,
1592          'category_id' => $category_id,
1593          'rank' => $rank,
[17424]1594          );
1595      }
[1121]1596    }
1597  }
[1592]1598
[17424]1599  if (count($inserts))
1600  {
1601    mass_inserts(
1602      IMAGE_CATEGORY_TABLE,
1603      array_keys($inserts[0]),
1604      $inserts
1605      );
[1121]1606
[17424]1607    update_category($categories);
1608  }
[1121]1609}
1610
1611/**
[17424]1612 * Dissociate images from all old categories except their storage category and
[13077]1613 * associate to new categories.
[26807]1614 * This function will preserve ranks.
[13077]1615 *
[26807]1616 * @param int[] $images
1617 * @param int[] $categories
[13077]1618 */
1619function move_images_to_categories($images, $categories)
1620{
1621  if (count($images) == 0)
1622  {
1623    return false;
1624  }
[17302]1625
[17424]1626  // let's first break links with all old albums but their "storage album"
[13077]1627  $query = '
1628DELETE '.IMAGE_CATEGORY_TABLE.'.*
1629  FROM '.IMAGE_CATEGORY_TABLE.'
1630    JOIN '.IMAGES_TABLE.' ON image_id=id
1631  WHERE id IN ('.implode(',', $images).')
[17980]1632';
[18628]1633
[17980]1634  if (is_array($categories) and count($categories) > 0)
1635  {
1636    $query.= '
1637    AND category_id NOT IN ('.implode(',', $categories).')
1638';
1639  }
1640
1641  $query.= '
[13077]1642    AND (storage_category_id IS NULL OR storage_category_id != category_id)
1643;';
1644  pwg_query($query);
[17302]1645
[13077]1646  if (is_array($categories) and count($categories) > 0)
1647  {
1648    associate_images_to_categories($images, $categories);
1649  }
1650}
1651
1652/**
[1121]1653 * Associate images associated to a list of source categories to a list of
1654 * destination categories.
1655 *
[26807]1656 * @param int[] $sources
1657 * @param int[] $destinations
[1121]1658 */
1659function associate_categories_to_categories($sources, $destinations)
1660{
1661  if (count($sources) == 0)
1662  {
1663    return false;
1664  }
1665
1666  $query = '
1667SELECT image_id
1668  FROM '.IMAGE_CATEGORY_TABLE.'
1669  WHERE category_id IN ('.implode(',', $sources).')
1670;';
1671  $images = array_from_query($query, 'image_id');
1672
1673  associate_images_to_categories($images, $destinations);
1674}
[1682]1675
1676/**
[2339]1677 * Refer main Piwigo URLs (currently PHPWG_DOMAIN domain)
[1724]1678 *
[26807]1679 * @return string[]
[1724]1680 */
1681function pwg_URL()
1682{
1683  $urls = array(
[12875]1684    'HOME'       => PHPWG_URL,
1685    'WIKI'       => PHPWG_URL.'/doc',
1686    'DEMO'       => PHPWG_URL.'/demo',
1687    'FORUM'      => PHPWG_URL.'/forum',
1688    'BUGS'       => PHPWG_URL.'/bugs',
1689    'EXTENSIONS' => PHPWG_URL.'/ext',
[1724]1690    );
1691  return $urls;
1692}
1693
[1978]1694/**
[26807]1695 * Invalidates cached data (permissions and category counts) for all users.
[1978]1696 */
[2890]1697function invalidate_user_cache($full = true)
[1978]1698{
[2890]1699  if ($full)
1700  {
1701    $query = '
1702TRUNCATE TABLE '.USER_CACHE_CATEGORIES_TABLE.';';
1703    pwg_query($query);
1704    $query = '
1705TRUNCATE TABLE '.USER_CACHE_TABLE.';';
1706    pwg_query($query);
1707  }
1708  else
1709  {
1710    $query = '
[1978]1711UPDATE '.USER_CACHE_TABLE.'
[2890]1712  SET need_update = \'true\';';
1713    pwg_query($query);
1714  }
[2892]1715  trigger_action('invalidate_user_cache', $full);
[1978]1716}
[2127]1717
[26807]1718/**
1719 * Invalidates cached tags counter for all users.
1720 */
[21817]1721function invalidate_user_cache_nb_tags()
1722{
1723  global $user;
1724  unset($user['nb_available_tags']);
[26807]1725
[21817]1726  $query = '
1727UPDATE '.USER_CACHE_TABLE.'
1728  SET nb_available_tags = NULL';
1729  pwg_query($query);
1730}
1731
[2127]1732/**
[26807]1733 * Adds the caracter set to a create table sql query.
1734 * All CREATE TABLE queries must call this function
1735 *
1736 * @param string $query
1737 * @return string
[2127]1738 */
1739function create_table_add_character_set($query)
1740{
[2502]1741  defined('DB_CHARSET') or fatal_error('create_table_add_character_set DB_CHARSET undefined');
[2127]1742  if ('DB_CHARSET'!='')
1743  {
[18634]1744    if ( version_compare(pwg_get_db_version(), '4.1.0', '<') )
[2127]1745    {
1746      return $query;
1747    }
1748    $charset_collate = " DEFAULT CHARACTER SET ".DB_CHARSET;
[2488]1749    if (DB_COLLATE!='')
[2127]1750    {
1751      $charset_collate .= " COLLATE ".DB_COLLATE;
1752    }
[2488]1753    if ( is_array($query) )
[2127]1754    {
[2488]1755      foreach( $query as $id=>$q)
1756      {
1757        $q=trim($q);
1758        $q=trim($q, ';');
1759        if (preg_match('/^CREATE\s+TABLE/i',$q))
1760        {
1761          $q.=$charset_collate;
1762        }
1763        $q .= ';';
1764        $query[$id] = $q;
1765      }
[2127]1766    }
[2488]1767    else
1768    {
1769      $query=trim($query);
1770      $query=trim($query, ';');
1771      if (preg_match('/^CREATE\s+TABLE/i',$query))
1772      {
1773        $query.=$charset_collate;
1774      }
1775      $query .= ';';
1776    }
[2127]1777  }
1778  return $query;
1779}
[2325]1780
1781/**
[26807]1782 * Returns access levels as array used on template with html_options functions.
1783 *
1784 * @param int $MinLevelAccess
1785 * @param int $MaxLevelAccess
1786 * @return array
[2325]1787 */
1788function get_user_access_level_html_options($MinLevelAccess = ACCESS_FREE, $MaxLevelAccess = ACCESS_CLOSED)
1789{
1790  $tpl_options = array();
1791  for ($level = $MinLevelAccess; $level <= $MaxLevelAccess; $level++)
1792  {
1793    $tpl_options[$level] = l10n(sprintf('ACCESS_%d', $level));
1794  }
1795  return $tpl_options;
1796}
1797
[2588]1798/**
[26807]1799 * returns a list of templates currently available in template-extension.
[2588]1800 * Each .tpl file is extracted from template-extension.
[26807]1801 *
1802 * @param string $start (internal use)
1803 * @return string[]
[2588]1804 */
1805function get_extents($start='')
1806{
1807  if ($start == '') { $start = './template-extension'; }
1808  $dir = opendir($start);
1809  $extents = array();
1810
1811  while (($file = readdir($dir)) !== false)
1812  {
1813    if ( $file == '.' or $file == '..' or $file == '.svn') continue;
1814    $path = $start . '/' . $file;
1815    if (is_dir($path))
1816    {
1817      $extents = array_merge($extents, get_extents($path));
1818    }
[3145]1819    elseif ( !is_link($path) and file_exists($path)
[2588]1820            and get_extension($path) == 'tpl' )
1821    {
1822      $extents[] = substr($path, 21);
1823    }
1824  }
1825  return $extents;
1826}
1827
[26807]1828/**
1829 * Create a new tag.
1830 *
1831 * @param string $tag_name
1832 * @return array ('id', info') or ('error')
1833 */
[2634]1834function create_tag($tag_name)
1835{
1836  // does the tag already exists?
1837  $query = '
1838SELECT id
1839  FROM '.TAGS_TABLE.'
1840  WHERE name = \''.$tag_name.'\'
1841;';
1842  $existing_tags = array_from_query($query, 'id');
1843
1844  if (count($existing_tags) == 0)
1845  {
[25019]1846    single_insert(
[2634]1847      TAGS_TABLE,
1848      array(
[25019]1849        'name' => $tag_name,
1850        'url_name' => trigger_event('render_tag_url', $tag_name),
[2634]1851        )
1852      );
1853
[4892]1854    $inserted_id = pwg_db_insert_id(TAGS_TABLE);
[2634]1855
1856    return array(
[25019]1857      'info' => l10n('Tag "%s" was added', stripslashes($tag_name)),
[2634]1858      'id' => $inserted_id,
1859      );
1860  }
1861  else
1862  {
1863    return array(
[25019]1864      'error' => l10n('Tag "%s" already exists', stripslashes($tag_name))
[2634]1865      );
1866  }
1867}
[2777]1868
[11828]1869/**
1870 * Is the category accessible to the (Admin) user ?
1871 * Note : if the user is not authorized to see this category, category jump
1872 * will be replaced by admin cat_modify page
1873 *
[26807]1874 * @param int $category_id
[11828]1875 * @return bool
1876 */
1877function cat_admin_access($category_id)
1878{
1879  global $user;
[2777]1880
[11828]1881  // $filter['visible_categories'] and $filter['visible_images']
1882  // are not used because it's not necessary (filter <> restriction)
1883  if (in_array($category_id, explode(',', $user['forbidden_categories'])))
1884  {
1885    return false;
1886  }
1887  return true;
1888}
1889
[2880]1890/**
[26807]1891 * Retrieve data from external URL.
[2880]1892 *
[26807]1893 * @param string $src
1894 * @param string|Ressource $dest - can be a file ressource or string
1895 * @param array $get_data - data added to request url
1896 * @param array $post_data - data transmitted with POST
1897 * @param string $user_agent
1898 * @param int $step (internal use)
[2880]1899 * @return bool
1900 */
[8079]1901function fetchRemote($src, &$dest, $get_data=array(), $post_data=array(), $user_agent='Piwigo', $step=0)
[2880]1902{
[2902]1903  // Try to retrieve data from local file?
1904  if (!url_is_remote($src))
1905  {
1906    $content = @file_get_contents($src);
1907    if ($content !== false)
1908    {
1909      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1910      return true;
1911    }
1912    else
1913    {
1914      return false;
1915    }
1916  }
1917
[8079]1918  // After 3 redirections, return false
1919  if ($step > 3) return false;
1920
[8080]1921  // Initialization
1922  $method  = empty($post_data) ? 'GET' : 'POST';
1923  $request = empty($post_data) ? '' : http_build_query($post_data, '', '&');
[8090]1924  if (!empty($get_data))
1925  {
1926    $src .= strpos($src, '?') === false ? '?' : '&';
1927    $src .= http_build_query($get_data, '', '&');
1928  }
[8080]1929
[2900]1930  // Initialize $dest
[2880]1931  is_resource($dest) or $dest = '';
1932
1933  // Try curl to read remote file
1934  if (function_exists('curl_init'))
1935  {
1936    $ch = @curl_init();
1937    @curl_setopt($ch, CURLOPT_URL, $src);
[2900]1938    @curl_setopt($ch, CURLOPT_HEADER, 1);
[2880]1939    @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
[2900]1940    @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
[8079]1941    if ($method == 'POST')
1942    {
1943      @curl_setopt($ch, CURLOPT_POST, 1);
[8090]1944      @curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
[8079]1945    }
[2880]1946    $content = @curl_exec($ch);
[2900]1947    $header_length = @curl_getinfo($ch, CURLINFO_HEADER_SIZE);
[2902]1948    $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
[2880]1949    @curl_close($ch);
[2902]1950    if ($content !== false and $status >= 200 and $status < 400)
[2880]1951    {
[2900]1952      if (preg_match('/Location:\s+?(.+)/', substr($content, 0, $header_length), $m))
1953      {
[8079]1954        return fetchRemote($m[1], $dest, array(), array(), $user_agent, $step+1);
[2900]1955      }
1956      $content = substr($content, $header_length);
1957      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
[2880]1958      return true;
1959    }
1960  }
1961
1962  // Try file_get_contents to read remote file
1963  if (ini_get('allow_url_fopen'))
1964  {
[8079]1965    $opts = array(
1966      'http' => array(
1967        'method' => $method,
1968        'user_agent' => $user_agent,
1969      )
1970    );
[8090]1971    if ($method == 'POST')
1972    {
1973      $opts['http']['content'] = $request;
1974    }
[8079]1975    $context = @stream_context_create($opts);
1976    $content = @file_get_contents($src, false, $context);
[2880]1977    if ($content !== false)
1978    {
1979      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1980      return true;
1981    }
1982  }
1983
1984  // Try fsockopen to read remote file
1985  $src = parse_url($src);
1986  $host = $src['host'];
1987  $path = isset($src['path']) ? $src['path'] : '/';
1988  $path .= isset($src['query']) ? '?'.$src['query'] : '';
[3145]1989
[2880]1990  if (($s = @fsockopen($host,80,$errno,$errstr,5)) === false)
1991  {
1992    return false;
1993  }
1994
[8079]1995  $http_request  = $method." ".$path." HTTP/1.0\r\n";
1996  $http_request .= "Host: ".$host."\r\n";
[8090]1997  if ($method == 'POST')
1998  {
1999    $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
2000    $http_request .= "Content-Length: ".strlen($request)."\r\n";
2001  }
[8079]2002  $http_request .= "User-Agent: ".$user_agent."\r\n";
2003  $http_request .= "Accept: */*\r\n";
2004  $http_request .= "\r\n";
2005  $http_request .= $request;
[2880]2006
[8079]2007  fwrite($s, $http_request);
2008
[2880]2009  $i = 0;
2010  $in_content = false;
2011  while (!feof($s))
2012  {
2013    $line = fgets($s);
2014
2015    if (rtrim($line,"\r\n") == '' && !$in_content)
2016    {
2017      $in_content = true;
2018      $i++;
2019      continue;
2020    }
2021    if ($i == 0)
2022    {
2023      if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/',rtrim($line,"\r\n"), $m))
2024      {
2025        fclose($s);
2026        return false;
2027      }
2028      $status = (integer) $m[2];
2029      if ($status < 200 || $status >= 400)
2030      {
2031        fclose($s);
2032        return false;
2033      }
2034    }
2035    if (!$in_content)
2036    {
2037      if (preg_match('/Location:\s+?(.+)$/',rtrim($line,"\r\n"),$m))
2038      {
2039        fclose($s);
[8079]2040        return fetchRemote(trim($m[1]),$dest,array(),array(),$user_agent,$step+1);
[2880]2041      }
2042      $i++;
2043      continue;
2044    }
2045    is_resource($dest) ? @fwrite($dest, $line) : $dest .= $line;
2046    $i++;
2047  }
2048  fclose($s);
2049  return true;
2050}
2051
[3145]2052/**
[26807]2053 * Returns the groupname corresponding to the given group identifier if exists.
[3145]2054 *
[26807]2055 * @param int $group_id
2056 * @return string|false
[3145]2057 */
2058function get_groupname($group_id)
2059{
2060  $query = '
2061SELECT name
2062  FROM '.GROUPS_TABLE.'
2063  WHERE id = '.intval($group_id).'
2064;';
2065  $result = pwg_query($query);
[4325]2066  if (pwg_db_num_rows($result) > 0)
[3145]2067  {
[4325]2068    list($groupname) = pwg_db_fetch_row($result);
[3145]2069  }
2070  else
2071  {
2072    return false;
2073  }
2074
2075  return $groupname;
2076}
2077
2078/**
[26807]2079 * Returns the username corresponding to the given user identifier if exists.
[3145]2080 *
[26807]2081 * @param int $user_id
2082 * @return string|false
[3145]2083 */
2084function get_username($user_id)
2085{
2086  global $conf;
2087
2088  $query = '
2089SELECT '.$conf['user_fields']['username'].'
2090  FROM '.USERS_TABLE.'
2091  WHERE '.$conf['user_fields']['id'].' = '.intval($user_id).'
2092;';
2093  $result = pwg_query($query);
[4325]2094  if (pwg_db_num_rows($result) > 0)
[3145]2095  {
[4325]2096    list($username) = pwg_db_fetch_row($result);
[3145]2097  }
2098  else
2099  {
2100    return false;
2101  }
2102
[4304]2103  return stripslashes($username);
[3145]2104}
[3382]2105
[26807]2106/**
2107 * Get url on piwigo.org for newsletter subscription
2108 *
2109 * @param string $language (unused)
2110 * @return string
2111 */
2112function get_newsletter_subscribe_base_url($language='en_UK')
2113{
[12875]2114  return PHPWG_URL.'/announcement/subscribe/';
[3382]2115}
[5173]2116
2117/**
[26807]2118 * Return admin menu id for accordion.
2119 *
2120 * @param string $menu_page
2121 * @return int
[5173]2122 */
2123function get_active_menu($menu_page)
2124{
2125  global $page;
2126
2127  if (isset($page['active_menu']))
2128  {
2129    return $page['active_menu'];
2130  }
2131
2132  switch ($menu_page)
2133  {
[13077]2134    case 'photo':
[5173]2135    case 'photos_add':
2136    case 'rating':
2137    case 'tags':
[8417]2138    case 'batch_manager':
[5173]2139      return 0;
2140
[13013]2141    case 'album':
[5173]2142    case 'cat_list':
2143    case 'cat_move':
2144    case 'cat_options':
2145    case 'permalinks':
2146      return 1;
[5691]2147
[5173]2148    case 'user_list':
2149    case 'user_perm':
2150    case 'group_list':
2151    case 'group_perm':
2152    case 'notification_by_mail':
2153      return 2;
2154
[10594]2155    case 'plugins':
[5173]2156    case 'plugin':
2157      return 3;
[5691]2158
[6056]2159    case 'site_manager':
2160    case 'site_update':
[5173]2161    case 'stats':
2162    case 'history':
2163    case 'maintenance':
[5997]2164    case 'comments':
[10511]2165    case 'updates':
[5173]2166      return 4;
2167
2168    case 'configuration':
[12820]2169    case 'derivatives':
[5173]2170    case 'extend_for_templates':
2171    case 'menubar':
[10594]2172    case 'themes':
[5448]2173    case 'theme':
[10594]2174    case 'languages':
[5173]2175      return 5;
[26807]2176
2177    default:
2178      return 0;
[5173]2179  }
2180}
[5188]2181
[26807]2182/**
2183 * Get tags list from SQL query (ids are surrounded by ~~, for get_tag_ids()).
2184 *
2185 * @param string $query
2186 * @param boolean $only_user_language - if true, only local name is returned for
2187 *    multilingual tags (if ExtendedDescription plugin is active)
2188 * @return array[] ('id', 'name')
[20805]2189 */
[12259]2190function get_taglist($query, $only_user_language=true)
[5188]2191{
2192  $result = pwg_query($query);
[12917]2193
[5188]2194  $taglist = array();
[17765]2195  $altlist = array();
[5188]2196  while ($row = pwg_db_fetch_assoc($result))
2197  {
[17765]2198    $raw_name = $row['name'];
[26649]2199    $name = trigger_event('render_tag_name', $raw_name, $row);
[17765]2200
2201    $taglist[] =  array(
2202        'name' => $name,
2203        'id' => '~~'.$row['id'].'~~',
2204      );
2205
2206    if (!$only_user_language)
[11487]2207    {
[17765]2208      $alt_names = trigger_event('get_tag_alt_names', array(), $raw_name);
2209
2210      foreach( array_diff( array_unique($alt_names), array($name) ) as $alt)
2211      {
2212        $altlist[] =  array(
2213            'name' => $alt,
[11853]2214            'id' => '~~'.$row['id'].'~~',
[11487]2215          );
2216      }
2217    }
[17765]2218  }
[12917]2219
[17765]2220  usort($taglist, 'tag_alpha_compare');
2221  if (count($altlist))
2222  {
2223    usort($altlist, 'tag_alpha_compare');
2224    $taglist = array_merge($taglist, $altlist);
[5188]2225  }
[12917]2226
[5188]2227  return $taglist;
2228}
2229
[26807]2230/**
2231 * Get tags ids from a list of raw tags (existing tags or new tags).
2232 *
2233 * In $raw_tags we receive something like array('~~6~~', '~~59~~', 'New
2234 * tag', 'Another new tag') The ~~34~~ means that it is an existing
2235 * tag. We added the surrounding ~~ to permit creation of tags like "10"
2236 * or "1234" (numeric characters only)
2237 *
2238 * @param string|string[] $raw_tags - array or comma separated string
2239 * @param boolean $allow_create
2240 * @return int[]
[20805]2241 */
[11853]2242function get_tag_ids($raw_tags, $allow_create=true)
[5188]2243{
2244  $tag_ids = array();
[20805]2245  if (!is_array($raw_tags))
2246  {
2247    $raw_tags = explode(',',$raw_tags);
2248  }
[5691]2249
[5188]2250  foreach ($raw_tags as $raw_tag)
2251  {
2252    if (preg_match('/^~~(\d+)~~$/', $raw_tag, $matches))
2253    {
[17724]2254      $tag_ids[] = $matches[1];
[5188]2255    }
[11853]2256    elseif ($allow_create)
[5188]2257    {
2258      // we have to create a new tag
[11853]2259      $tag_ids[] = tag_id_from_tag_name($raw_tag);
[5188]2260    }
2261  }
2262
2263  return $tag_ids;
2264}
[8247]2265
[26807]2266/**
2267 * Returns the argument_ids array with new sequenced keys based on related
[8247]2268 * names. Sequence is not case sensitive.
[26807]2269 * Warning: By definition, this function breaks original keys.
2270 *
2271 * @param int[] $elements_ids
2272 * @param string[] $name - names of elements, indexed by ids
2273 * @return int[]
[8247]2274 */
[26807]2275function order_by_name($element_ids, $name)
[8247]2276{
2277  $ordered_element_ids = array();
2278  foreach ($element_ids as $k_id => $element_id)
2279  {
2280    $key = strtolower($name[$element_id]) .'-'. $name[$element_id] .'-'. $k_id;
2281    $ordered_element_ids[$key] = $element_id;
2282  }
2283  ksort($ordered_element_ids);
2284  return $ordered_element_ids;
2285}
2286
[26807]2287/**
2288 * Grant access to a list of categories for a list of users.
2289 *
2290 * @param int[] $category_ids
2291 * @param int[] $user_ids
2292 */
[11728]2293function add_permission_on_category($category_ids, $user_ids)
2294{
2295  if (!is_array($category_ids))
2296  {
2297    $category_ids = array($category_ids);
2298  }
2299  if (!is_array($user_ids))
2300  {
2301    $user_ids = array($user_ids);
2302  }
2303
2304  // check for emptiness
2305  if (count($category_ids) == 0 or count($user_ids) == 0)
2306  {
2307    return;
2308  }
[12917]2309
[12019]2310  // make sure categories are private and select uppercats or subcats
[25244]2311  $cat_ids = get_uppercat_ids($category_ids);
2312  if (isset($_POST['apply_on_sub']))
2313  {
2314    $cat_ids = array_merge($cat_ids, get_subcat_ids($category_ids));
2315  }
2316
[11728]2317  $query = '
[25244]2318SELECT id
[11728]2319  FROM '.CATEGORIES_TABLE.'
[25244]2320  WHERE id IN ('.implode(',', $cat_ids).')
[11728]2321    AND status = \'private\'
2322;';
[12019]2323  $private_cats = array_from_query($query, 'id');
[11728]2324
[12019]2325  if (count($private_cats) == 0)
[11728]2326  {
2327    return;
2328  }
[25244]2329 
[11728]2330  $inserts = array();
[12019]2331  foreach ($private_cats as $cat_id)
[11728]2332  {
[25244]2333    foreach ($user_ids as $user_id)
[11728]2334    {
[25018]2335      $inserts[] = array(
2336        'user_id' => $user_id,
2337        'cat_id' => $cat_id
[11728]2338        );
2339    }
2340  }
[25244]2341 
2342  mass_inserts(
2343    USER_ACCESS_TABLE,
2344    array('user_id','cat_id'),
2345    $inserts,
2346    array('ignore'=>true)
2347    );
[11728]2348}
2349
[26807]2350/**
2351 * Returns the list of admin users.
2352 *
2353 * @param boolean $include_webmaster
2354 * @return int[]
2355 */
[11728]2356function get_admins($include_webmaster=true)
2357{
2358  $status_list = array('admin');
2359
2360  if ($include_webmaster)
2361  {
2362    $status_list[] = 'webmaster';
2363  }
[12917]2364
[11728]2365  $query = '
2366SELECT
2367    user_id
2368  FROM '.USER_INFOS_TABLE.'
2369  WHERE status in (\''.implode("','", $status_list).'\')
2370;';
2371
2372  return array_from_query($query, 'user_id');
2373}
[12797]2374
[26807]2375/**
2376 * Delete all derivative files for one or several types
2377 *
2378 * @param 'all'|int[] $types
2379 */
[12820]2380function clear_derivative_cache($types='all')
[12797]2381{
[17302]2382  if ($types === 'all')
[12797]2383  {
[12820]2384    $types = ImageStdParams::get_all_types();
2385    $types[] = IMG_CUSTOM;
2386  }
2387  elseif (!is_array($types))
2388  {
2389    $types = array($types);
2390  }
2391
[17302]2392  for ($i=0; $i<count($types); $i++)
[12820]2393  {
[17302]2394    $type = $types[$i];
2395    if ($type == IMG_CUSTOM)
[12797]2396    {
[17302]2397      $type = derivative_to_url($type).'[a-zA-Z0-9]+';
[12797]2398    }
[17302]2399    elseif (in_array($type, ImageStdParams::get_all_types()))
2400    {
2401      $type = derivative_to_url($type);
2402    }
2403    else
2404    {//assume a custom type
2405      $type = derivative_to_url(IMG_CUSTOM).'_'.$type;
2406    }
2407    $types[$i] = $type;
[12797]2408  }
[17302]2409
2410  $pattern='#.*-';
2411  if (count($types)>1)
2412  {
2413    $pattern .= '(' . implode('|',$types) . ')';
2414  }
[12797]2415  else
2416  {
[17302]2417    $pattern .= $types[0];
[12797]2418  }
[17302]2419  $pattern.='\.[a-zA-Z0-9]{3,4}$#';
[12917]2420
[13651]2421  if ($contents = @opendir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR))
[12797]2422  {
2423    while (($node = readdir($contents)) !== false)
2424    {
2425      if ($node != '.'
2426          and $node != '..'
2427          and is_dir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node))
2428      {
2429        clear_derivative_cache_rec(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node, $pattern);
2430      }
2431    }
2432    closedir($contents);
2433  }
2434}
2435
[26807]2436/**
2437 * Used by clear_derivative_cache()
2438 * @ignore
2439 */
[12797]2440function clear_derivative_cache_rec($path, $pattern)
2441{
2442  $rmdir = true;
2443  $rm_index = false;
2444
2445  if ($contents = opendir($path))
2446  {
2447    while (($node = readdir($contents)) !== false)
2448    {
2449      if ($node == '.' or $node == '..')
2450        continue;
2451      if (is_dir($path.'/'.$node))
2452      {
2453        $rmdir &= clear_derivative_cache_rec($path.'/'.$node, $pattern);
2454      }
2455      else
2456      {
2457        if (preg_match($pattern, $node))
2458        {
2459          unlink($path.'/'.$node);
2460        }
2461        elseif ($node=='index.htm')
2462        {
2463          $rm_index = true;
2464        }
2465        else
2466        {
2467          $rmdir = false;
2468        }
2469      }
2470    }
2471    closedir($contents);
[12917]2472
[12797]2473    if ($rmdir)
2474    {
2475      if ($rm_index)
2476      {
2477        unlink($path.'/index.htm');
2478      }
2479      clearstatcache();
[12820]2480      @rmdir($path);
[12797]2481    }
2482    return $rmdir;
2483  }
2484}
[12906]2485
[26807]2486/**
2487 * Deletes derivatives of a particular element
2488 *
2489 * @param array $infos ('path'[, 'representative_ext'])
2490 * @param 'all'|int $type
2491 */
[13052]2492function delete_element_derivatives($infos, $type='all')
[12906]2493{
[12917]2494  $path = $infos['path'];
2495  if (!empty($infos['representative_ext']))
[12906]2496  {
[12917]2497    $path = original_to_representative( $path, $infos['representative_ext']);
[12906]2498  }
[12917]2499  if (substr_compare($path, '../', 0, 3)==0)
2500  {
2501    $path = substr($path, 3);
2502  }
[13052]2503  $dot = strrpos($path, '.');
2504  if ($type=='all')
2505  {
2506    $pattern = '-*';
2507  }
2508  else
2509  {
2510    $pattern = '-'.derivative_to_url($type).'*';
2511  }
2512  $path = substr_replace($path, $pattern, $dot, 0);
[16167]2513  if ( ($glob=glob(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$path)) !== false)
[12917]2514  {
[16167]2515    foreach( $glob as $file)
2516    {
2517      @unlink($file);
2518    }
[12917]2519  }
[12906]2520}
[21567]2521
2522/**
[26807]2523 * Returns an array containing sub-directories, excluding ".svn"
[21567]2524 *
[26807]2525 * @param string $directory
2526 * @return string[]
[21567]2527 */
2528function get_dirs($directory)
2529{
2530  $sub_dirs = array();
2531  if ($opendir = opendir($directory))
2532  {
2533    while ($file = readdir($opendir))
2534    {
2535      if ($file != '.'
2536          and $file != '..'
2537          and is_dir($directory.'/'.$file)
2538          and $file != '.svn')
2539      {
[25018]2540        $sub_dirs[] = $file;
[21567]2541      }
2542    }
2543    closedir($opendir);
2544  }
2545  return $sub_dirs;
2546}
[23821]2547
2548/**
[26807]2549 * Recursively delete a directory.
2550 *
[23821]2551 * @param string $path
2552 * @param string $trash_path, try to move the directory to this path if it cannot be delete
2553 */
2554function deltree($path, $trash_path=null)
2555{
2556  if (is_dir($path))
2557  {
2558    $fh = opendir($path);
2559    while ($file = readdir($fh))
2560    {
2561      if ($file != '.' and $file != '..')
2562      {
2563        $pathfile = $path . '/' . $file;
2564        if (is_dir($pathfile))
2565        {
2566          deltree($pathfile, $trash_path);
2567        }
2568        else
2569        {
2570          @unlink($pathfile);
2571        }
2572      }
2573    }
2574    closedir($fh);
2575   
2576    if (@rmdir($path))
2577    {
2578      return true;
2579    }
2580    elseif (!empty($trash_path))
2581    {
2582      if (!is_dir($trash_path))
2583      {
2584        @mkgetdir($trash_path, MKGETDIR_RECURSIVE|MKGETDIR_DIE_ON_ERROR|MKGETDIR_PROTECT_HTACCESS);
2585      }
2586      while ($r = $trash_path . '/' . md5(uniqid(rand(), true)))
2587      {
2588        if (!is_dir($r))
2589        {
2590          @rename($path, $r);
2591          break;
2592        }
2593      }
2594    }
2595    else
2596    {
2597      return false;
2598    }
2599  }
2600}
2601
[8247]2602?>
Note: See TracBrowser for help on using the repository browser.