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

Last change on this file since 8848 was 8848, checked in by plg, 10 years ago

bug 2119 fixed: no crash when calling pwg.categories.delete on an empty album

  • Property svn:eol-style set to LF
File size: 49.1 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2011 Piwigo Team                  http://piwigo.org |
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// +-----------------------------------------------------------------------+
23
24include(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
25
26// The function delete_site deletes a site and call the function
27// delete_categories for each primary category of the site
28function delete_site( $id )
29{
30  // destruction of the categories of the site
31  $query = '
32SELECT id
33  FROM '.CATEGORIES_TABLE.'
34  WHERE site_id = '.$id.'
35;';
36  $result = pwg_query($query);
37  $category_ids = array();
38  while ($row = pwg_db_fetch_assoc($result))
39  {
40    array_push($category_ids, $row['id']);
41  }
42  delete_categories($category_ids);
43
44  // destruction of the site
45  $query = '
46DELETE FROM '.SITES_TABLE.'
47  WHERE id = '.$id.'
48;';
49  pwg_query($query);
50}
51
52
53// The function delete_categories deletes the categories identified by the
54// (numeric) key of the array $ids. It also deletes (in the database) :
55//    - all the elements physically linked to the category (delete_elements, see further)
56//    - all the links between elements and this category
57//    - all the restrictions linked to the category
58// The function works recursively.
59//
60// the $photo_deletion_mode is for photos virtually linked to the categorty
61//   * no_delete : delete no photo, may create orphans
62//   * delete_orphans : delete photos that are no longer linked to any category
63//   * force_delete : delete photos even if they are linked to another category
64function delete_categories($ids, $photo_deletion_mode='no_delete')
65{
66  if (count($ids) == 0)
67  {
68    return;
69  }
70
71  // add sub-category ids to the given ids : if a category is deleted, all
72  // sub-categories must be so
73  $ids = get_subcat_ids($ids);
74
75  // destruction of all photos physically linked to the category
76  $query = '
77SELECT id
78  FROM '.IMAGES_TABLE.'
79  WHERE storage_category_id IN (
80'.wordwrap(implode(', ', $ids), 80, "\n").')
81;';
82  $result = pwg_query($query);
83  $element_ids = array();
84  while ($row = pwg_db_fetch_assoc($result))
85  {
86    array_push($element_ids, $row['id']);
87  }
88  delete_elements($element_ids);
89
90  // now, should we delete photos that are virtually linked to the category?
91  if ('delete_orphans' == $photo_deletion_mode or 'force_delete' == $photo_deletion_mode)
92  {
93    $query = '
94SELECT
95    DISTINCT(image_id)
96  FROM '.IMAGE_CATEGORY_TABLE.'
97  WHERE category_id IN ('.implode(',', $ids).')
98;';
99    $image_ids_linked = array_from_query($query, 'image_id');
100
101    if (count($image_ids_linked) > 0)
102    {
103      if ('delete_orphans' == $photo_deletion_mode)
104      {
105        $query = '
106SELECT
107    DISTINCT(image_id)
108  FROM '.IMAGE_CATEGORY_TABLE.'
109  WHERE image_id IN ('.implode(',', $image_ids_linked).')
110    AND category_id NOT IN ('.implode(',', $ids).')
111;';
112        $image_ids_not_orphans = array_from_query($query, 'image_id');
113        $image_ids_to_delete = array_diff($image_ids_linked, $image_ids_not_orphans);
114      }
115
116      if ('force_delete' == $photo_deletion_mode)
117      {
118        $image_ids_to_delete = $image_ids_linked;
119      }
120
121      delete_elements($image_ids_to_delete, true);
122    }
123  }
124
125  // destruction of the links between images and this category
126  $query = '
127DELETE FROM '.IMAGE_CATEGORY_TABLE.'
128  WHERE category_id IN (
129'.wordwrap(implode(', ', $ids), 80, "\n").')
130;';
131  pwg_query($query);
132
133  // destruction of the access linked to the category
134  $query = '
135DELETE FROM '.USER_ACCESS_TABLE.'
136  WHERE cat_id IN (
137'.wordwrap(implode(', ', $ids), 80, "\n").')
138;';
139  pwg_query($query);
140
141  $query = '
142DELETE FROM '.GROUP_ACCESS_TABLE.'
143  WHERE cat_id IN (
144'.wordwrap(implode(', ', $ids), 80, "\n").')
145;';
146  pwg_query($query);
147
148  // destruction of the category
149  $query = '
150DELETE FROM '.CATEGORIES_TABLE.'
151  WHERE id IN (
152'.wordwrap(implode(', ', $ids), 80, "\n").')
153;';
154  pwg_query($query);
155
156  $query='
157DELETE FROM '.OLD_PERMALINKS_TABLE.'
158  WHERE cat_id IN ('.implode(',',$ids).')';
159  pwg_query($query);
160
161  $query='
162DELETE FROM '.USER_CACHE_CATEGORIES_TABLE.'
163  WHERE cat_id IN ('.implode(',',$ids).')';
164  pwg_query($query);
165
166  trigger_action('delete_categories', $ids);
167}
168
169// The function delete_elements deletes the elements identified by the
170// (numeric) values of the array $ids. It also deletes (in the database) :
171//    - all the comments related to elements
172//    - all the links between categories and elements
173//    - all the favorites associated to elements
174// @return number of deleted elements
175function delete_elements($ids, $physical_deletion=false)
176{
177  if (count($ids) == 0)
178  {
179    return 0;
180  }
181  trigger_action('begin_delete_elements', $ids);
182
183  if ($physical_deletion)
184  {
185    include_once(PHPWG_ROOT_PATH.'include/functions_picture.inc.php');
186    $new_ids=array();
187
188    $query = '
189SELECT
190    id,
191    path,
192    tn_ext,
193    has_high,
194    representative_ext
195  FROM '.IMAGES_TABLE.'
196  WHERE id IN ('.implode(',', $ids).')
197;';
198    $result = pwg_query($query);
199    while ($row = pwg_db_fetch_assoc($result))
200    {
201      if (url_is_remote($row['path']))
202        continue;
203      $files = array();
204      $files[] = get_element_path($row);
205      if (!empty($row['tn_ext']))
206        $files[] = get_thumbnail_path($row);
207      if (!empty($row['has_high']) and get_boolean($row['has_high']))
208        $files[] = get_high_path($row);
209      if (!empty($row['representative_ext']))
210      {
211        $pi = pathinfo($row['path']);
212        $file_wo_ext = get_filename_wo_extension($pi['basename']);
213        $files[] = PHPWG_ROOT_PATH.$pi['dirname'].'/pwg_representative/'.$file_wo_ext.'.'.$element_info['representative_ext'];
214      }
215
216      $ok = true;
217      foreach ($files as $path)
218      {
219        if (is_file($path) and !unlink($path))
220        {
221          $ok = false;
222          trigger_error('"'.$path.'" cannot be removed', E_USER_WARNING);
223          break;
224        }
225      }
226      if ($ok)
227        $new_ids[] += $row['id'];
228      else
229        break;
230    }
231    $ids = $new_ids;
232    if (count($ids)==0)
233    {
234      return 0;
235    }
236  }
237
238  // destruction of the comments on the image
239  $query = '
240DELETE FROM '.COMMENTS_TABLE.'
241  WHERE image_id IN (
242'.wordwrap(implode(', ', $ids), 80, "\n").')
243;';
244  pwg_query($query);
245
246  // destruction of the links between images and this category
247  $query = '
248DELETE FROM '.IMAGE_CATEGORY_TABLE.'
249  WHERE image_id IN (
250'.wordwrap(implode(', ', $ids), 80, "\n").')
251;';
252  pwg_query($query);
253
254  // destruction of the links between images and tags
255  $query = '
256DELETE FROM '.IMAGE_TAG_TABLE.'
257  WHERE image_id IN (
258'.wordwrap(implode(', ', $ids), 80, "\n").')
259;';
260  pwg_query($query);
261
262  // destruction of the favorites associated with the picture
263  $query = '
264DELETE FROM '.FAVORITES_TABLE.'
265  WHERE image_id IN (
266'.wordwrap(implode(', ', $ids), 80, "\n").')
267;';
268  pwg_query($query);
269
270  // destruction of the rates associated to this element
271  $query = '
272DELETE FROM '.RATE_TABLE.'
273  WHERE element_id IN (
274'.wordwrap(implode(', ', $ids), 80, "\n").')
275;';
276  pwg_query($query);
277
278  // destruction of the rates associated to this element
279  $query = '
280DELETE FROM '.CADDIE_TABLE.'
281  WHERE element_id IN (
282'.wordwrap(implode(', ', $ids), 80, "\n").')
283;';
284  pwg_query($query);
285
286  // destruction of the image
287  $query = '
288DELETE FROM '.IMAGES_TABLE.'
289  WHERE id IN (
290'.wordwrap(implode(', ', $ids), 80, "\n").')
291;';
292  pwg_query($query);
293
294  // are the photo used as category representant?
295  $query = '
296SELECT
297    id
298  FROM '.CATEGORIES_TABLE.'
299  WHERE representative_picture_id IN (
300'.wordwrap(implode(', ', $ids), 80, "\n").')
301;';
302  $category_ids = array_from_query($query, 'id');
303  if (count($category_ids) > 0)
304  {
305    update_category($category_ids);
306  }
307
308  trigger_action('delete_elements', $ids);
309  return count($ids);
310}
311
312// The delete_user function delete a user identified by the $user_id
313// It also deletes :
314//     - all the access linked to this user
315//     - all the links to any group
316//     - all the favorites linked to this user
317//     - calculated permissions linked to the user
318//     - all datas about notifications for the user
319function delete_user($user_id)
320{
321  global $conf;
322  $tables = array(
323    // destruction of the access linked to the user
324    USER_ACCESS_TABLE,
325    // destruction of data notification by mail for this user
326    USER_MAIL_NOTIFICATION_TABLE,
327    // destruction of data RSS notification for this user
328    USER_FEED_TABLE,
329    // deletion of calculated permissions linked to the user
330    USER_CACHE_TABLE,
331    // deletion of computed cache data linked to the user
332    USER_CACHE_CATEGORIES_TABLE,
333    // destruction of the group links for this user
334    USER_GROUP_TABLE,
335    // destruction of the favorites associated with the user
336    FAVORITES_TABLE,
337    // destruction of the caddie associated with the user
338    CADDIE_TABLE,
339    // deletion of piwigo specific informations
340    USER_INFOS_TABLE,
341    );
342
343  foreach ($tables as $table)
344  {
345    $query = '
346DELETE FROM '.$table.'
347  WHERE user_id = '.$user_id.'
348;';
349    pwg_query($query);
350  }
351
352  // destruction of the user
353  $query = '
354DELETE FROM '.SESSIONS_TABLE.'
355  WHERE data LIKE \'pwg_uid|i:'.(int)$user_id.';%\'
356;';
357  pwg_query($query);
358
359  // destruction of the user
360  $query = '
361DELETE FROM '.USERS_TABLE.'
362  WHERE '.$conf['user_fields']['id'].' = '.$user_id.'
363;';
364  pwg_query($query);
365
366  trigger_action('delete_user', $user_id);
367}
368
369/**
370 * Deletes all tags linked to no photo
371 */
372function delete_orphan_tags()
373{
374  $orphan_tags = get_orphan_tags();
375 
376  if (count($orphan_tags) > 0)
377  {
378    $orphan_tag_ids = array();
379    foreach ($orphan_tags as $tag)
380    {
381      array_push($orphan_tag_ids, $tag['id']);
382    }
383
384    $query = '
385DELETE
386  FROM '.TAGS_TABLE.'
387  WHERE id IN ('.implode(',', $orphan_tag_ids).')
388;';
389    pwg_query($query);
390  }
391}
392
393/**
394 * Get all tags (id + name) linked to no photo
395 */
396function get_orphan_tags()
397{
398  $orphan_tags = array();
399 
400  $query = '
401SELECT
402    id,
403    name
404  FROM '.TAGS_TABLE.'
405    LEFT JOIN '.IMAGE_TAG_TABLE.' ON id = tag_id
406  WHERE tag_id IS NULL
407;';
408  $result = pwg_query($query);
409  while ($row = pwg_db_fetch_assoc($result))
410  {
411    array_push($orphan_tags, $row);
412  }
413
414  return $orphan_tags;
415}
416
417/**
418 * Verifies that the representative picture really exists in the db and
419 * picks up a random represantive if possible and based on config.
420 *
421 * @param mixed category id
422 * @returns void
423 */
424function update_category($ids = 'all')
425{
426  global $conf;
427
428  if ($ids=='all')
429  {
430    $where_cats = '1=1';
431  }
432  elseif ( !is_array($ids) )
433  {
434    $where_cats = '%s='.$ids;
435  }
436  else
437  {
438    if (count($ids) == 0)
439    {
440      return false;
441    }
442    $where_cats = '%s IN('.wordwrap(implode(', ', $ids), 120, "\n").')';
443  }
444
445  // find all categories where the setted representative is not possible :
446  // the picture does not exist
447  $query = '
448SELECT DISTINCT c.id
449  FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i
450    ON c.representative_picture_id = i.id
451  WHERE representative_picture_id IS NOT NULL
452    AND '.sprintf($where_cats, 'c.id').'
453    AND i.id IS NULL
454;';
455  $wrong_representant = array_from_query($query, 'id');
456
457  if (count($wrong_representant) > 0)
458  {
459    $query = '
460UPDATE '.CATEGORIES_TABLE.'
461  SET representative_picture_id = NULL
462  WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 120, "\n").')
463;';
464    pwg_query($query);
465  }
466
467  if (!$conf['allow_random_representative'])
468  {
469    // If the random representant is not allowed, we need to find
470    // categories with elements and with no representant. Those categories
471    // must be added to the list of categories to set to a random
472    // representant.
473    $query = '
474SELECT DISTINCT id
475  FROM '.CATEGORIES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.'
476    ON id = category_id
477  WHERE representative_picture_id IS NULL
478    AND '.sprintf($where_cats, 'category_id').'
479;';
480    $to_rand = array_from_query($query, 'id');
481    if (count($to_rand) > 0)
482    {
483      set_random_representant($to_rand);
484    }
485  }
486}
487
488/**
489 * returns an array containing sub-directories which can be a category,
490 * recursive by default
491 *
492 * directories nammed "thumbnail", "pwg_high" or "pwg_representative" are
493 * omitted
494 *
495 * @param string $basedir
496 * @return array
497 */
498function get_fs_directories($path, $recursive = true)
499{
500  $dirs = array();
501
502  if (is_dir($path))
503  {
504    if ($contents = opendir($path))
505    {
506      while (($node = readdir($contents)) !== false)
507      {
508        if (is_dir($path.'/'.$node)
509            and $node != '.'
510            and $node != '..'
511            and $node != '.svn'
512            and $node != 'thumbnail'
513            and $node != 'pwg_high'
514            and $node != 'pwg_representative')
515        {
516          array_push($dirs, $path.'/'.$node);
517          if ($recursive)
518          {
519            $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node));
520          }
521        }
522      }
523      closedir($contents);
524    }
525  }
526
527  return $dirs;
528}
529
530/**
531 * order categories (update categories.rank and global_rank database fields)
532 * so that rank field are consecutive integers starting at 1 for each child
533 * @return void
534 */
535function update_global_rank()
536{
537  $query = '
538SELECT id, id_uppercat, uppercats, rank, global_rank
539  FROM '.CATEGORIES_TABLE.'
540  ORDER BY id_uppercat,rank,name';
541
542  $cat_map = array();
543
544  $current_rank = 0;
545  $current_uppercat = '';
546
547  $result = pwg_query($query);
548  while ($row = pwg_db_fetch_assoc($result))
549  {
550    if ($row['id_uppercat'] != $current_uppercat)
551    {
552      $current_rank = 0;
553      $current_uppercat = $row['id_uppercat'];
554    }
555    ++$current_rank;
556    $cat =
557      array(
558        'rank' =>        $current_rank,
559        'rank_changed' =>$current_rank!=$row['rank'],
560        'global_rank' => $row['global_rank'],
561        'uppercats' =>   $row['uppercats'],
562        );
563    $cat_map[ $row['id'] ] = $cat;
564  }
565
566  $datas = array();
567
568  foreach( $cat_map as $id=>$cat )
569  {
570    $new_global_rank = preg_replace(
571          '/(\d+)/e',
572          "\$cat_map['$1']['rank']",
573          str_replace(',', '.', $cat['uppercats'] )
574          );
575    if ( $cat['rank_changed']
576      or $new_global_rank!=$cat['global_rank']
577      )
578    {
579      $datas[] = array(
580          'id' => $id,
581          'rank' => $cat['rank'],
582          'global_rank' => $new_global_rank,
583        );
584    }
585  }
586
587  mass_updates(
588    CATEGORIES_TABLE,
589    array(
590      'primary' => array('id'),
591      'update'  => array('rank', 'global_rank')
592      ),
593    $datas
594    );
595  return count($datas);
596}
597
598/**
599 * change the visible property on a set of categories
600 *
601 * @param array categories
602 * @param string value
603 * @return void
604 */
605function set_cat_visible($categories, $value)
606{
607  if (!in_array($value, array('true', 'false')))
608  {
609    trigger_error("set_cat_visible invalid param $value", E_USER_WARNING);
610    return false;
611  }
612
613  // unlocking a category => all its parent categories become unlocked
614  if ($value == 'true')
615  {
616    $uppercats = get_uppercat_ids($categories);
617    $query = '
618UPDATE '.CATEGORIES_TABLE.'
619  SET visible = \'true\'
620  WHERE id IN ('.implode(',', $uppercats).')';
621    pwg_query($query);
622  }
623  // locking a category   => all its child categories become locked
624  if ($value == 'false')
625  {
626    $subcats = get_subcat_ids($categories);
627    $query = '
628UPDATE '.CATEGORIES_TABLE.'
629  SET visible = \'false\'
630  WHERE id IN ('.implode(',', $subcats).')';
631    pwg_query($query);
632  }
633}
634
635/**
636 * change the status property on a set of categories : private or public
637 *
638 * @param array categories
639 * @param string value
640 * @return void
641 */
642function set_cat_status($categories, $value)
643{
644  if (!in_array($value, array('public', 'private')))
645  {
646    trigger_error("set_cat_status invalid param $value", E_USER_WARNING);
647    return false;
648  }
649
650  // make public a category => all its parent categories become public
651  if ($value == 'public')
652  {
653    $uppercats = get_uppercat_ids($categories);
654    $query = '
655UPDATE '.CATEGORIES_TABLE.'
656  SET status = \'public\'
657  WHERE id IN ('.implode(',', $uppercats).')
658;';
659    pwg_query($query);
660  }
661  // make a category private => all its child categories become private
662  if ($value == 'private')
663  {
664    $subcats = get_subcat_ids($categories);
665    $query = '
666UPDATE '.CATEGORIES_TABLE.'
667  SET status = \'private\'
668  WHERE id IN ('.implode(',', $subcats).')';
669    pwg_query($query);
670  }
671}
672
673/**
674 * returns all uppercats category ids of the given category ids
675 *
676 * @param array cat_ids
677 * @return array
678 */
679function get_uppercat_ids($cat_ids)
680{
681  if (!is_array($cat_ids) or count($cat_ids) < 1)
682  {
683    return array();
684  }
685
686  $uppercats = array();
687
688  $query = '
689SELECT uppercats
690  FROM '.CATEGORIES_TABLE.'
691  WHERE id IN ('.implode(',', $cat_ids).')
692;';
693  $result = pwg_query($query);
694  while ($row = pwg_db_fetch_assoc($result))
695  {
696    $uppercats = array_merge($uppercats,
697                             explode(',', $row['uppercats']));
698  }
699  $uppercats = array_unique($uppercats);
700
701  return $uppercats;
702}
703
704/**
705 * set a new random representant to the categories
706 *
707 * @param array categories
708 */
709function set_random_representant($categories)
710{
711  $datas = array();
712  foreach ($categories as $category_id)
713  {
714    $query = '
715SELECT image_id
716  FROM '.IMAGE_CATEGORY_TABLE.'
717  WHERE category_id = '.$category_id.'
718  ORDER BY '.DB_RANDOM_FUNCTION.'()
719  LIMIT 1
720;';
721    list($representative) = pwg_db_fetch_row(pwg_query($query));
722
723    array_push(
724      $datas,
725      array(
726        'id' => $category_id,
727        'representative_picture_id' => $representative,
728        )
729      );
730  }
731
732  mass_updates(
733    CATEGORIES_TABLE,
734    array(
735      'primary' => array('id'),
736      'update' => array('representative_picture_id')
737      ),
738    $datas
739    );
740}
741
742/**
743 * returns the fulldir for each given category id
744 *
745 * @param array cat_ids
746 * @return array
747 */
748function get_fulldirs($cat_ids)
749{
750  if (count($cat_ids) == 0)
751  {
752    return array();
753  }
754
755  // caching directories of existing categories
756  $query = '
757SELECT id, dir
758  FROM '.CATEGORIES_TABLE.'
759  WHERE dir IS NOT NULL
760;';
761  $cat_dirs = simple_hash_from_query($query, 'id', 'dir');
762
763  // caching galleries_url
764  $query = '
765SELECT id, galleries_url
766  FROM '.SITES_TABLE.'
767;';
768  $galleries_url = simple_hash_from_query($query, 'id', 'galleries_url');
769
770  // categories : id, site_id, uppercats
771  $categories = array();
772
773  $query = '
774SELECT id, uppercats, site_id
775  FROM '.CATEGORIES_TABLE.'
776  WHERE dir IS NOT NULL
777    AND id IN (
778'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
779;';
780  $result = pwg_query($query);
781  while ($row = pwg_db_fetch_assoc($result))
782  {
783    array_push($categories, $row);
784  }
785
786  // filling $cat_fulldirs
787  $cat_fulldirs = array();
788  foreach ($categories as $category)
789  {
790    $uppercats = str_replace(',', '/', $category['uppercats']);
791    $cat_fulldirs[$category['id']] = $galleries_url[$category['site_id']];
792    $cat_fulldirs[$category['id']].= preg_replace('/(\d+)/e',
793                                                  "\$cat_dirs['$1']",
794                                                  $uppercats);
795  }
796
797  return $cat_fulldirs;
798}
799
800/**
801 * returns an array with all file system files according to
802 * $conf['file_ext']
803 *
804 * @param string $path
805 * @param bool recursive
806 * @return array
807 */
808function get_fs($path, $recursive = true)
809{
810  global $conf;
811
812  // because isset is faster than in_array...
813  if (!isset($conf['flip_picture_ext']))
814  {
815    $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
816  }
817  if (!isset($conf['flip_file_ext']))
818  {
819    $conf['flip_file_ext'] = array_flip($conf['file_ext']);
820  }
821
822  $fs['elements'] = array();
823  $fs['thumbnails'] = array();
824  $fs['representatives'] = array();
825  $subdirs = array();
826
827  if (is_dir($path))
828  {
829    if ($contents = opendir($path))
830    {
831      while (($node = readdir($contents)) !== false)
832      {
833        if (is_file($path.'/'.$node))
834        {
835          $extension = get_extension($node);
836
837//          if (in_array($extension, $conf['picture_ext']))
838          if (isset($conf['flip_picture_ext'][$extension]))
839          {
840            if (basename($path) == 'thumbnail')
841            {
842              array_push($fs['thumbnails'], $path.'/'.$node);
843            }
844            else if (basename($path) == 'pwg_representative')
845            {
846              array_push($fs['representatives'], $path.'/'.$node);
847            }
848            else
849            {
850              array_push($fs['elements'], $path.'/'.$node);
851            }
852          }
853//          else if (in_array($extension, $conf['file_ext']))
854          else if (isset($conf['flip_file_ext'][$extension]))
855          {
856            array_push($fs['elements'], $path.'/'.$node);
857          }
858        }
859        else if (is_dir($path.'/'.$node)
860                 and $node != '.'
861                 and $node != '..'
862                 and $node != 'pwg_high'
863                 and $recursive)
864        {
865          array_push($subdirs, $node);
866        }
867      }
868    }
869    closedir($contents);
870
871    foreach ($subdirs as $subdir)
872    {
873      $tmp_fs = get_fs($path.'/'.$subdir);
874
875      $fs['elements']        = array_merge($fs['elements'],
876                                           $tmp_fs['elements']);
877
878      $fs['thumbnails']      = array_merge($fs['thumbnails'],
879                                           $tmp_fs['thumbnails']);
880
881      $fs['representatives'] = array_merge($fs['representatives'],
882                                           $tmp_fs['representatives']);
883    }
884  }
885  return $fs;
886}
887
888/**
889 * synchronize base users list and related users list
890 *
891 * compares and synchronizes base users table (USERS_TABLE) with its child
892 * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each
893 * base user must be present in child tables, users in child tables not
894 * present in base table must be deleted.
895 *
896 * @return void
897 */
898function sync_users()
899{
900  global $conf;
901
902  $query = '
903SELECT '.$conf['user_fields']['id'].' AS id
904  FROM '.USERS_TABLE.'
905;';
906  $base_users = array_from_query($query, 'id');
907
908  $query = '
909SELECT user_id
910  FROM '.USER_INFOS_TABLE.'
911;';
912  $infos_users = array_from_query($query, 'user_id');
913
914  // users present in $base_users and not in $infos_users must be added
915  $to_create = array_diff($base_users, $infos_users);
916
917  if (count($to_create) > 0)
918  {
919    create_user_infos($to_create);
920  }
921
922  // users present in user related tables must be present in the base user
923  // table
924  $tables = array(
925    USER_MAIL_NOTIFICATION_TABLE,
926    USER_FEED_TABLE,
927    USER_INFOS_TABLE,
928    USER_ACCESS_TABLE,
929    USER_CACHE_TABLE,
930    USER_CACHE_CATEGORIES_TABLE,
931    USER_GROUP_TABLE
932    );
933
934  foreach ($tables as $table)
935  {
936    $query = '
937SELECT DISTINCT user_id
938  FROM '.$table.'
939;';
940    $to_delete = array_diff(
941      array_from_query($query, 'user_id'),
942      $base_users
943      );
944
945    if (count($to_delete) > 0)
946    {
947      $query = '
948DELETE
949  FROM '.$table.'
950  WHERE user_id in ('.implode(',', $to_delete).')
951;';
952      pwg_query($query);
953    }
954  }
955}
956
957/**
958 * updates categories.uppercats field based on categories.id +
959 * categories.id_uppercat
960 *
961 * @return void
962 */
963function update_uppercats()
964{
965  $query = '
966SELECT id, id_uppercat, uppercats
967  FROM '.CATEGORIES_TABLE.'
968;';
969  $cat_map = hash_from_query($query, 'id');
970
971  $datas = array();
972  foreach ($cat_map as $id => $cat)
973  {
974    $upper_list = array();
975
976    $uppercat = $id;
977    while ($uppercat)
978    {
979      array_push($upper_list, $uppercat);
980      $uppercat = $cat_map[$uppercat]['id_uppercat'];
981    }
982
983    $new_uppercats = implode(',', array_reverse($upper_list));
984    if ($new_uppercats != $cat['uppercats'])
985    {
986      array_push(
987        $datas,
988        array(
989          'id' => $id,
990          'uppercats' => $new_uppercats
991          )
992        );
993    }
994  }
995  $fields = array('primary' => array('id'), 'update' => array('uppercats'));
996  mass_updates(CATEGORIES_TABLE, $fields, $datas);
997}
998
999/**
1000 * update images.path field
1001 *
1002 * @return void
1003 */
1004function update_path()
1005{
1006  $query = '
1007SELECT DISTINCT(storage_category_id)
1008  FROM '.IMAGES_TABLE.'
1009  WHERE storage_category_id IS NOT NULL
1010;';
1011  $cat_ids = array_from_query($query, 'storage_category_id');
1012  $fulldirs = get_fulldirs($cat_ids);
1013
1014  foreach ($cat_ids as $cat_id)
1015  {
1016    $query = '
1017UPDATE '.IMAGES_TABLE.'
1018  SET path = '.pwg_db_concat(array("'".$fulldirs[$cat_id]."/'",'file')).'
1019  WHERE storage_category_id = '.$cat_id.'
1020;';
1021    pwg_query($query);
1022  }
1023}
1024
1025/**
1026 * update images.average_rate field
1027 * param int $element_id optional, otherwise applies to all
1028 * @return void
1029 */
1030function update_average_rate( $element_id=-1 )
1031{
1032  $query = '
1033SELECT element_id,
1034       ROUND(AVG(rate),2) AS average_rate
1035  FROM '.RATE_TABLE;
1036  if ( $element_id != -1 )
1037  {
1038    $query .= ' WHERE element_id=' . $element_id;
1039  }
1040  $query .= ' GROUP BY element_id;';
1041
1042  $result = pwg_query($query);
1043
1044  $datas = array();
1045
1046  while ($row = pwg_db_fetch_assoc($result))
1047  {
1048    array_push(
1049      $datas,
1050      array(
1051        'id' => $row['element_id'],
1052        'average_rate' => $row['average_rate']
1053        )
1054      );
1055  }
1056
1057  mass_updates(
1058    IMAGES_TABLE,
1059    array(
1060      'primary' => array('id'),
1061      'update' => array('average_rate')
1062      ),
1063    $datas
1064    );
1065
1066  $query='
1067SELECT id FROM '.IMAGES_TABLE .'
1068  LEFT JOIN '.RATE_TABLE.' ON id=element_id
1069  WHERE element_id IS NULL AND average_rate IS NOT NULL';
1070  if ( $element_id != -1 )
1071  {
1072    $query .= ' AND id=' . $element_id;
1073  }
1074  $to_update = array_from_query( $query, 'id');
1075
1076  if ( !empty($to_update) )
1077  {
1078    $query='
1079UPDATE '.IMAGES_TABLE .'
1080  SET average_rate=NULL
1081  WHERE id IN (' . implode(',',$to_update) . ')';
1082    pwg_query($query);
1083  }
1084}
1085
1086/**
1087 * change the parent category of the given categories. The categories are
1088 * supposed virtual.
1089 *
1090 * @param array category identifiers
1091 * @param int parent category identifier
1092 * @return void
1093 */
1094function move_categories($category_ids, $new_parent = -1)
1095{
1096  global $page;
1097
1098  if (count($category_ids) == 0)
1099  {
1100    return;
1101  }
1102
1103  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
1104
1105  $categories = array();
1106
1107  $query = '
1108SELECT id, id_uppercat, status, uppercats
1109  FROM '.CATEGORIES_TABLE.'
1110  WHERE id IN ('.implode(',', $category_ids).')
1111;';
1112  $result = pwg_query($query);
1113  while ($row = pwg_db_fetch_assoc($result))
1114  {
1115    $categories[$row['id']] =
1116      array(
1117        'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
1118        'status' => $row['status'],
1119        'uppercats' => $row['uppercats']
1120        );
1121  }
1122
1123  // is the movement possible? The movement is impossible if you try to move
1124  // a category in a sub-category or itself
1125  if ('NULL' != $new_parent)
1126  {
1127    $query = '
1128SELECT uppercats
1129  FROM '.CATEGORIES_TABLE.'
1130  WHERE id = '.$new_parent.'
1131;';
1132    list($new_parent_uppercats) = pwg_db_fetch_row(pwg_query($query));
1133
1134    foreach ($categories as $category)
1135    {
1136      // technically, you can't move a category with uppercats 12,125,13,14
1137      // into a new parent category with uppercats 12,125,13,14,24
1138      if (preg_match('/^'.$category['uppercats'].',/', $new_parent_uppercats))
1139      {
1140        array_push(
1141          $page['errors'],
1142          l10n('You cannot move an album in its own sub album')
1143          );
1144        return;
1145      }
1146    }
1147  }
1148
1149  $tables =
1150    array(
1151      USER_ACCESS_TABLE => 'user_id',
1152      GROUP_ACCESS_TABLE => 'group_id'
1153      );
1154
1155  $query = '
1156UPDATE '.CATEGORIES_TABLE.'
1157  SET id_uppercat = '.$new_parent.'
1158  WHERE id IN ('.implode(',', $category_ids).')
1159;';
1160  pwg_query($query);
1161
1162  update_uppercats();
1163  update_global_rank();
1164
1165  // status and related permissions management
1166  if ('NULL' == $new_parent)
1167  {
1168    $parent_status = 'public';
1169  }
1170  else
1171  {
1172    $query = '
1173SELECT status
1174  FROM '.CATEGORIES_TABLE.'
1175  WHERE id = '.$new_parent.'
1176;';
1177    list($parent_status) = pwg_db_fetch_row(pwg_query($query));
1178  }
1179
1180  if ('private' == $parent_status)
1181  {
1182    foreach ($categories as $cat_id => $category)
1183    {
1184      switch ($category['status'])
1185      {
1186        case 'public' :
1187        {
1188          set_cat_status(array($cat_id), 'private');
1189          break;
1190        }
1191        case 'private' :
1192        {
1193          $subcats = get_subcat_ids(array($cat_id));
1194
1195          foreach ($tables as $table => $field)
1196          {
1197            $query = '
1198SELECT '.$field.'
1199  FROM '.$table.'
1200  WHERE cat_id = '.$cat_id.'
1201;';
1202            $category_access = array_from_query($query, $field);
1203
1204            $query = '
1205SELECT '.$field.'
1206  FROM '.$table.'
1207  WHERE cat_id = '.$new_parent.'
1208;';
1209            $parent_access = array_from_query($query, $field);
1210
1211            $to_delete = array_diff($parent_access, $category_access);
1212
1213            if (count($to_delete) > 0)
1214            {
1215              $query = '
1216DELETE FROM '.$table.'
1217  WHERE '.$field.' IN ('.implode(',', $to_delete).')
1218    AND cat_id IN ('.implode(',', $subcats).')
1219;';
1220              pwg_query($query);
1221            }
1222          }
1223          break;
1224        }
1225      }
1226    }
1227  }
1228
1229  array_push(
1230    $page['infos'],
1231    l10n_dec(
1232      '%d album moved', '%d albums moved',
1233      count($categories)
1234      )
1235    );
1236}
1237
1238/**
1239 * create a virtual category
1240 *
1241 * @param string category name
1242 * @param int parent category id
1243 * @return array with ('info' and 'id') or ('error') key
1244 */
1245function create_virtual_category($category_name, $parent_id=null)
1246{
1247  global $conf;
1248
1249  // is the given category name only containing blank spaces ?
1250  if (preg_match('/^\s*$/', $category_name))
1251  {
1252    return array('error' => l10n('The name of an album must not be empty'));
1253  }
1254
1255  $parent_id = !empty($parent_id) ? $parent_id : 'NULL';
1256
1257  $query = '
1258SELECT MAX(rank)
1259  FROM '.CATEGORIES_TABLE.'
1260  WHERE id_uppercat '.(is_numeric($parent_id) ? '= '.$parent_id : 'IS NULL').'
1261;';
1262  list($current_rank) = pwg_db_fetch_row(pwg_query($query));
1263
1264  $insert = array(
1265    'name' => $category_name,
1266    'rank' => ++$current_rank,
1267    'commentable' => boolean_to_string($conf['newcat_default_commentable']),
1268    );
1269
1270  if ($parent_id != 'NULL')
1271  {
1272    $query = '
1273SELECT id, uppercats, global_rank, visible, status
1274  FROM '.CATEGORIES_TABLE.'
1275  WHERE id = '.$parent_id.'
1276;';
1277    $parent = pwg_db_fetch_assoc(pwg_query($query));
1278
1279    $insert['id_uppercat'] = $parent['id'];
1280    $insert['global_rank'] = $parent['global_rank'].'.'.$insert['rank'];
1281
1282    // at creation, must a category be visible or not ? Warning : if the
1283    // parent category is invisible, the category is automatically create
1284    // invisible. (invisible = locked)
1285    if ('false' == $parent['visible'])
1286    {
1287      $insert['visible'] = 'false';
1288    }
1289    else
1290    {
1291      $insert['visible'] = boolean_to_string($conf['newcat_default_visible']);
1292    }
1293
1294    // at creation, must a category be public or private ? Warning : if the
1295    // parent category is private, the category is automatically create
1296    // private.
1297    if ('private' == $parent['status'])
1298    {
1299      $insert['status'] = 'private';
1300    }
1301    else
1302    {
1303      $insert['status'] = $conf['newcat_default_status'];
1304    }
1305  }
1306  else
1307  {
1308    $insert['visible'] = boolean_to_string($conf['newcat_default_visible']);
1309    $insert['status'] = $conf['newcat_default_status'];
1310    $insert['global_rank'] = $insert['rank'];
1311  }
1312
1313  // we have then to add the virtual category
1314  mass_inserts(
1315    CATEGORIES_TABLE,
1316    array(
1317      'site_id', 'name', 'id_uppercat', 'rank', 'commentable',
1318      'visible', 'status', 'global_rank',
1319      ),
1320    array($insert)
1321    );
1322
1323  $inserted_id = pwg_db_insert_id(CATEGORIES_TABLE);
1324
1325  $query = '
1326UPDATE
1327  '.CATEGORIES_TABLE.'
1328  SET uppercats = \''.
1329    (isset($parent) ? $parent{'uppercats'}.',' : '').
1330    $inserted_id.
1331    '\'
1332  WHERE id = '.$inserted_id.'
1333;';
1334  pwg_query($query);
1335
1336  return array(
1337    'info' => l10n('Virtual album added'),
1338    'id'   => $inserted_id,
1339    );
1340}
1341
1342/**
1343 * Set tags to an image. Warning: given tags are all tags associated to the
1344 * image, not additionnal tags.
1345 *
1346 * @param array tag ids
1347 * @param int image id
1348 * @return void
1349 */
1350function set_tags($tags, $image_id)
1351{
1352  $query = '
1353DELETE
1354  FROM '.IMAGE_TAG_TABLE.'
1355  WHERE image_id = '.$image_id.'
1356;';
1357  pwg_query($query);
1358
1359  if (count($tags) > 0)
1360  {
1361    $inserts = array();
1362    foreach ($tags as $tag_id)
1363    {
1364      array_push(
1365        $inserts,
1366        array(
1367          'tag_id' => $tag_id,
1368          'image_id' => $image_id
1369          )
1370        );
1371    }
1372    mass_inserts(
1373      IMAGE_TAG_TABLE,
1374      array_keys($inserts[0]),
1375      $inserts
1376      );
1377  }
1378}
1379
1380/**
1381 * Add new tags to a set of images.
1382 *
1383 * @param array tag ids
1384 * @param array image ids
1385 * @return void
1386 */
1387function add_tags($tags, $images)
1388{
1389  if (count($tags) == 0 or count($images) == 0)
1390  {
1391    return;
1392  }
1393
1394  // we can't insert twice the same {image_id,tag_id} so we must first
1395  // delete lines we'll insert later
1396  $query = '
1397DELETE
1398  FROM '.IMAGE_TAG_TABLE.'
1399  WHERE image_id IN ('.implode(',', $images).')
1400    AND tag_id IN ('.implode(',', $tags).')
1401;';
1402  pwg_query($query);
1403
1404  $inserts = array();
1405  foreach ($images as $image_id)
1406  {
1407    foreach ($tags as $tag_id)
1408    {
1409      array_push(
1410        $inserts,
1411        array(
1412          'image_id' => $image_id,
1413          'tag_id' => $tag_id,
1414          )
1415        );
1416    }
1417  }
1418  mass_inserts(
1419    IMAGE_TAG_TABLE,
1420    array_keys($inserts[0]),
1421    $inserts
1422    );
1423}
1424
1425function tag_id_from_tag_name($tag_name)
1426{
1427  global $page;
1428
1429  $tag_name = trim($tag_name);
1430  if (isset($page['tag_id_from_tag_name_cache'][$tag_name]))
1431  {
1432    return $page['tag_id_from_tag_name_cache'][$tag_name];
1433  }
1434
1435  // does the tag already exists?
1436  $query = '
1437SELECT id
1438  FROM '.TAGS_TABLE.'
1439  WHERE name = \''.$tag_name.'\'
1440;';
1441  $existing_tags = array_from_query($query, 'id');
1442
1443  if (count($existing_tags) == 0)
1444  {
1445    mass_inserts(
1446      TAGS_TABLE,
1447      array('name', 'url_name'),
1448      array(
1449        array(
1450          'name' => $tag_name,
1451          'url_name' => str2url($tag_name),
1452          )
1453        )
1454      );
1455
1456    $page['tag_id_from_tag_name_cache'][$tag_name] = pwg_db_insert_id(TAGS_TABLE);
1457  }
1458  else
1459  {
1460    $page['tag_id_from_tag_name_cache'][$tag_name] = $existing_tags[0];
1461  }
1462
1463  return $page['tag_id_from_tag_name_cache'][$tag_name];
1464}
1465
1466function set_tags_of($tags_of)
1467{
1468  if (count($tags_of) > 0)
1469  {
1470    $query = '
1471DELETE
1472  FROM '.IMAGE_TAG_TABLE.'
1473  WHERE image_id IN ('.implode(',', array_keys($tags_of)).')
1474;';
1475    pwg_query($query);
1476
1477    $inserts = array();
1478
1479    foreach ($tags_of as $image_id => $tag_ids)
1480    {
1481      foreach ($tag_ids as $tag_id)
1482      {
1483        array_push(
1484          $inserts,
1485          array(
1486            'image_id' => $image_id,
1487            'tag_id' => $tag_id,
1488            )
1489          );
1490      }
1491    }
1492
1493    mass_inserts(
1494      IMAGE_TAG_TABLE,
1495      array_keys($inserts[0]),
1496      $inserts
1497      );
1498  }
1499}
1500
1501/**
1502 * Associate a list of images to a list of categories.
1503 *
1504 * The function will not duplicate links
1505 *
1506 * @param array images
1507 * @param array categories
1508 * @return void
1509 */
1510function associate_images_to_categories($images, $categories)
1511{
1512  if (count($images) == 0
1513      or count($categories) == 0)
1514  {
1515    return false;
1516  }
1517
1518  $query = '
1519DELETE
1520  FROM '.IMAGE_CATEGORY_TABLE.'
1521  WHERE image_id IN ('.implode(',', $images).')
1522    AND category_id IN ('.implode(',', $categories).')
1523;';
1524  pwg_query($query);
1525
1526  $inserts = array();
1527  foreach ($categories as $category_id)
1528  {
1529    foreach ($images as $image_id)
1530    {
1531      array_push(
1532        $inserts,
1533        array(
1534          'image_id' => $image_id,
1535          'category_id' => $category_id,
1536          )
1537        );
1538    }
1539  }
1540
1541  mass_inserts(
1542    IMAGE_CATEGORY_TABLE,
1543    array_keys($inserts[0]),
1544    $inserts
1545    );
1546
1547  update_category($categories);
1548}
1549
1550/**
1551 * Associate images associated to a list of source categories to a list of
1552 * destination categories.
1553 *
1554 * @param array sources
1555 * @param array destinations
1556 * @return void
1557 */
1558function associate_categories_to_categories($sources, $destinations)
1559{
1560  if (count($sources) == 0)
1561  {
1562    return false;
1563  }
1564
1565  $query = '
1566SELECT image_id
1567  FROM '.IMAGE_CATEGORY_TABLE.'
1568  WHERE category_id IN ('.implode(',', $sources).')
1569;';
1570  $images = array_from_query($query, 'image_id');
1571
1572  associate_images_to_categories($images, $destinations);
1573}
1574
1575/**
1576 * Refer main Piwigo URLs (currently PHPWG_DOMAIN domain)
1577 *
1578 * @param void
1579 * @return array like $conf['links']
1580 */
1581function pwg_URL()
1582{
1583  $urls = array(
1584    'HOME'       => 'http://'.PHPWG_DOMAIN,
1585    'WIKI'       => 'http://'.PHPWG_DOMAIN.'/doc',
1586    'DEMO'       => 'http://'.PHPWG_DOMAIN.'/demo',
1587    'FORUM'      => 'http://'.PHPWG_DOMAIN.'/forum',
1588    'BUGS'       => 'http://'.PHPWG_DOMAIN.'/bugs',
1589    'EXTENSIONS' => 'http://'.PHPWG_DOMAIN.'/ext',
1590    );
1591  return $urls;
1592}
1593
1594/**
1595 * Invalidates cahed data (permissions and category counts) for all users.
1596 */
1597function invalidate_user_cache($full = true)
1598{
1599  if ($full)
1600  {
1601    $query = '
1602TRUNCATE TABLE '.USER_CACHE_CATEGORIES_TABLE.';';
1603    pwg_query($query);
1604    $query = '
1605TRUNCATE TABLE '.USER_CACHE_TABLE.';';
1606    pwg_query($query);
1607  }
1608  else
1609  {
1610    $query = '
1611UPDATE '.USER_CACHE_TABLE.'
1612  SET need_update = \'true\';';
1613    pwg_query($query);
1614  }
1615  trigger_action('invalidate_user_cache', $full);
1616}
1617
1618/**
1619 * adds the caracter set to a create table sql query.
1620 * all CREATE TABLE queries must call this function
1621 * @param string query - the sql query
1622 */
1623function create_table_add_character_set($query)
1624{
1625  defined('DB_CHARSET') or fatal_error('create_table_add_character_set DB_CHARSET undefined');
1626  if ('DB_CHARSET'!='')
1627  {
1628    if ( version_compare(mysql_get_server_info(), '4.1.0', '<') )
1629    {
1630      return $query;
1631    }
1632    $charset_collate = " DEFAULT CHARACTER SET ".DB_CHARSET;
1633    if (DB_COLLATE!='')
1634    {
1635      $charset_collate .= " COLLATE ".DB_COLLATE;
1636    }
1637    if ( is_array($query) )
1638    {
1639      foreach( $query as $id=>$q)
1640      {
1641        $q=trim($q);
1642        $q=trim($q, ';');
1643        if (preg_match('/^CREATE\s+TABLE/i',$q))
1644        {
1645          $q.=$charset_collate;
1646        }
1647        $q .= ';';
1648        $query[$id] = $q;
1649      }
1650    }
1651    else
1652    {
1653      $query=trim($query);
1654      $query=trim($query, ';');
1655      if (preg_match('/^CREATE\s+TABLE/i',$query))
1656      {
1657        $query.=$charset_collate;
1658      }
1659      $query .= ';';
1660    }
1661  }
1662  return $query;
1663}
1664
1665/**
1666 * Returns array use on template with html_options method
1667 * @param Min and Max access to use
1668 * @return array of user access level
1669 */
1670function get_user_access_level_html_options($MinLevelAccess = ACCESS_FREE, $MaxLevelAccess = ACCESS_CLOSED)
1671{
1672  $tpl_options = array();
1673  for ($level = $MinLevelAccess; $level <= $MaxLevelAccess; $level++)
1674  {
1675    $tpl_options[$level] = l10n(sprintf('ACCESS_%d', $level));
1676  }
1677  return $tpl_options;
1678}
1679
1680/**
1681 * returns a list of templates currently available in template-extension
1682 * Each .tpl file is extracted from template-extension.
1683 * @return array
1684 */
1685function get_extents($start='')
1686{
1687  if ($start == '') { $start = './template-extension'; }
1688  $dir = opendir($start);
1689  $extents = array();
1690
1691  while (($file = readdir($dir)) !== false)
1692  {
1693    if ( $file == '.' or $file == '..' or $file == '.svn') continue;
1694    $path = $start . '/' . $file;
1695    if (is_dir($path))
1696    {
1697      $extents = array_merge($extents, get_extents($path));
1698    }
1699    elseif ( !is_link($path) and file_exists($path)
1700            and get_extension($path) == 'tpl' )
1701    {
1702      $extents[] = substr($path, 21);
1703    }
1704  }
1705  return $extents;
1706}
1707
1708function create_tag($tag_name)
1709{
1710  // does the tag already exists?
1711  $query = '
1712SELECT id
1713  FROM '.TAGS_TABLE.'
1714  WHERE name = \''.$tag_name.'\'
1715;';
1716  $existing_tags = array_from_query($query, 'id');
1717
1718  if (count($existing_tags) == 0)
1719  {
1720    mass_inserts(
1721      TAGS_TABLE,
1722      array('name', 'url_name'),
1723      array(
1724        array(
1725          'name' => $tag_name,
1726          'url_name' => str2url($tag_name),
1727          )
1728        )
1729      );
1730
1731    $inserted_id = pwg_db_insert_id(TAGS_TABLE);
1732
1733    return array(
1734      'info' => sprintf(
1735        l10n('Tag "%s" was added'),
1736        stripslashes($tag_name)
1737        ),
1738      'id' => $inserted_id,
1739      );
1740  }
1741  else
1742  {
1743    return array(
1744      'error' => sprintf(
1745        l10n('Tag "%s" already exists'),
1746        stripslashes($tag_name)
1747        )
1748      );
1749  }
1750}
1751
1752/**
1753 * Is the category accessible to the (Admin) user ?
1754 *
1755 * Note : if the user is not authorized to see this category, category jump
1756 * will be replaced by admin cat_modify page
1757 *
1758 * @param int category id to verify
1759 * @return bool
1760 */
1761function cat_admin_access($category_id)
1762{
1763  global $user;
1764
1765  // $filter['visible_categories'] and $filter['visible_images']
1766  // are not used because it's not necessary (filter <> restriction)
1767  if (in_array($category_id, explode(',', $user['forbidden_categories'])))
1768  {
1769    return false;
1770  }
1771  return true;
1772}
1773
1774/**
1775 * Retrieve data from external URL
1776 *
1777 * @param string $src: URL
1778 * @param global $dest: can be a file ressource or string
1779 * @return bool
1780 */
1781function fetchRemote($src, &$dest, $get_data=array(), $post_data=array(), $user_agent='Piwigo', $step=0)
1782{
1783  // Try to retrieve data from local file?
1784  if (!url_is_remote($src))
1785  {
1786    $content = @file_get_contents($src);
1787    if ($content !== false)
1788    {
1789      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1790      return true;
1791    }
1792    else
1793    {
1794      return false;
1795    }
1796  }
1797
1798  // After 3 redirections, return false
1799  if ($step > 3) return false;
1800
1801  // Initialization
1802  $method  = empty($post_data) ? 'GET' : 'POST';
1803  $request = empty($post_data) ? '' : http_build_query($post_data, '', '&');
1804  if (!empty($get_data))
1805  {
1806    $src .= strpos($src, '?') === false ? '?' : '&';
1807    $src .= http_build_query($get_data, '', '&');
1808  }
1809
1810  // Initialize $dest
1811  is_resource($dest) or $dest = '';
1812
1813  // Try curl to read remote file
1814  if (function_exists('curl_init'))
1815  {
1816    $ch = @curl_init();
1817    @curl_setopt($ch, CURLOPT_URL, $src);
1818    @curl_setopt($ch, CURLOPT_HEADER, 1);
1819    @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
1820    @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1821    if ($method == 'POST')
1822    {
1823      @curl_setopt($ch, CURLOPT_POST, 1);
1824      @curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
1825    }
1826    $content = @curl_exec($ch);
1827    $header_length = @curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1828    $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
1829    @curl_close($ch);
1830    if ($content !== false and $status >= 200 and $status < 400)
1831    {
1832      if (preg_match('/Location:\s+?(.+)/', substr($content, 0, $header_length), $m))
1833      {
1834        return fetchRemote($m[1], $dest, array(), array(), $user_agent, $step+1);
1835      }
1836      $content = substr($content, $header_length);
1837      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1838      return true;
1839    }
1840  }
1841
1842  // Try file_get_contents to read remote file
1843  if (ini_get('allow_url_fopen'))
1844  {
1845    $opts = array(
1846      'http' => array(
1847        'method' => $method,
1848        'user_agent' => $user_agent,
1849      )
1850    );
1851    if ($method == 'POST')
1852    {
1853      $opts['http']['content'] = $request;
1854    }
1855    $context = @stream_context_create($opts);
1856    $content = @file_get_contents($src, false, $context);
1857    if ($content !== false)
1858    {
1859      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1860      return true;
1861    }
1862  }
1863
1864  // Try fsockopen to read remote file
1865  $src = parse_url($src);
1866  $host = $src['host'];
1867  $path = isset($src['path']) ? $src['path'] : '/';
1868  $path .= isset($src['query']) ? '?'.$src['query'] : '';
1869
1870  if (($s = @fsockopen($host,80,$errno,$errstr,5)) === false)
1871  {
1872    return false;
1873  }
1874
1875  $http_request  = $method." ".$path." HTTP/1.0\r\n";
1876  $http_request .= "Host: ".$host."\r\n";
1877  if ($method == 'POST')
1878  {
1879    $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
1880    $http_request .= "Content-Length: ".strlen($request)."\r\n";
1881  }
1882  $http_request .= "User-Agent: ".$user_agent."\r\n";
1883  $http_request .= "Accept: */*\r\n";
1884  $http_request .= "\r\n";
1885  $http_request .= $request;
1886
1887  fwrite($s, $http_request);
1888
1889  $i = 0;
1890  $in_content = false;
1891  while (!feof($s))
1892  {
1893    $line = fgets($s);
1894
1895    if (rtrim($line,"\r\n") == '' && !$in_content)
1896    {
1897      $in_content = true;
1898      $i++;
1899      continue;
1900    }
1901    if ($i == 0)
1902    {
1903      if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/',rtrim($line,"\r\n"), $m))
1904      {
1905        fclose($s);
1906        return false;
1907      }
1908      $status = (integer) $m[2];
1909      if ($status < 200 || $status >= 400)
1910      {
1911        fclose($s);
1912        return false;
1913      }
1914    }
1915    if (!$in_content)
1916    {
1917      if (preg_match('/Location:\s+?(.+)$/',rtrim($line,"\r\n"),$m))
1918      {
1919        fclose($s);
1920        return fetchRemote(trim($m[1]),$dest,array(),array(),$user_agent,$step+1);
1921      }
1922      $i++;
1923      continue;
1924    }
1925    is_resource($dest) ? @fwrite($dest, $line) : $dest .= $line;
1926    $i++;
1927  }
1928  fclose($s);
1929  return true;
1930}
1931
1932
1933/**
1934 * returns the groupname corresponding to the given group identifier if
1935 * exists
1936 *
1937 * @param int group_id
1938 * @return mixed
1939 */
1940function get_groupname($group_id)
1941{
1942  $query = '
1943SELECT name
1944  FROM '.GROUPS_TABLE.'
1945  WHERE id = '.intval($group_id).'
1946;';
1947  $result = pwg_query($query);
1948  if (pwg_db_num_rows($result) > 0)
1949  {
1950    list($groupname) = pwg_db_fetch_row($result);
1951  }
1952  else
1953  {
1954    return false;
1955  }
1956
1957  return $groupname;
1958}
1959
1960/**
1961 * returns the username corresponding to the given user identifier if exists
1962 *
1963 * @param int user_id
1964 * @return mixed
1965 */
1966function get_username($user_id)
1967{
1968  global $conf;
1969
1970  $query = '
1971SELECT '.$conf['user_fields']['username'].'
1972  FROM '.USERS_TABLE.'
1973  WHERE '.$conf['user_fields']['id'].' = '.intval($user_id).'
1974;';
1975  $result = pwg_query($query);
1976  if (pwg_db_num_rows($result) > 0)
1977  {
1978    list($username) = pwg_db_fetch_row($result);
1979  }
1980  else
1981  {
1982    return false;
1983  }
1984
1985  return stripslashes($username);
1986}
1987
1988function get_newsletter_subscribe_base_url($language) {
1989  $subscribe_domain = 'piwigo.org';
1990
1991  $domain_of = array(
1992    'fr_FR' => 'fr.piwigo.org',
1993    'it_IT' => 'it.piwigo.org',
1994    'de_DE' => 'de.piwigo.org',
1995    'es_ES' => 'es.piwigo.org',
1996    'zh_CN' => 'cn.piwigo.org',
1997    'pl_PL' => 'pl.piwigo.org',
1998    'hu_HU' => 'hu.piwigo.org',
1999    'ru_RU' => 'ru.piwigo.org',
2000        'nl_NL' => 'nl.piwigo.org',
2001    );
2002
2003  if (isset($domain_of[$language])) {
2004    $subscribe_domain = $domain_of[$language];
2005  }
2006
2007  return 'http://'.$subscribe_domain.'/announcement/subscribe/';
2008}
2009
2010/**
2011 * Accordion menus need to know which section to open by default when
2012 * loading the page
2013 */
2014function get_active_menu($menu_page)
2015{
2016  global $page;
2017
2018  if (isset($page['active_menu']))
2019  {
2020    return $page['active_menu'];
2021  }
2022
2023  switch ($menu_page)
2024  {
2025    case 'photos_add':
2026    case 'rating':
2027    case 'tags':
2028    case 'picture_modify':
2029    case 'batch_manager':
2030      return 0;
2031
2032    case 'cat_list':
2033    case 'cat_modify':
2034    case 'cat_move':
2035    case 'cat_options':
2036    case 'cat_perm':
2037    case 'permalinks':
2038      return 1;
2039
2040    case 'user_list':
2041    case 'user_perm':
2042    case 'group_list':
2043    case 'group_perm':
2044    case 'notification_by_mail':
2045      return 2;
2046
2047    case 'plugins_list':
2048    case 'plugins_update':
2049    case 'plugins_new':
2050    case 'plugin':
2051      return 3;
2052
2053    case 'site_manager':
2054    case 'site_update':
2055    case 'stats':
2056    case 'history':
2057    case 'maintenance':
2058    case 'thumbnail':
2059    case 'comments':
2060      return 4;
2061
2062    case 'configuration':
2063    case 'extend_for_templates':
2064    case 'menubar':
2065    case 'themes_new':
2066    case 'themes_installed':
2067    case 'theme':
2068    case 'languages_installed':
2069    case 'languages_new':
2070      return 5;
2071  }
2072  return 0;
2073}
2074
2075function get_fckb_taglist($query)
2076{
2077  $result = pwg_query($query);
2078  $taglist = array();
2079  while ($row = pwg_db_fetch_assoc($result))
2080  {
2081    array_push(
2082      $taglist,
2083      array(
2084        'caption' => $row['tag_name'],
2085        'value' => '~~'.$row['tag_id'].'~~',
2086        )
2087      );
2088  }
2089
2090  return $taglist;
2091}
2092
2093function get_fckb_tag_ids($raw_tags)
2094{
2095  // In $raw_tags we receive something like array('~~6~~', '~~59~~', 'New
2096  // tag', 'Another new tag') The ~~34~~ means that it is an existing
2097  // tag. I've added the surrounding ~~ to permit creation of tags like "10"
2098  // or "1234" (numeric characters only)
2099
2100  $tag_ids = array();
2101
2102  foreach ($raw_tags as $raw_tag)
2103  {
2104    if (preg_match('/^~~(\d+)~~$/', $raw_tag, $matches))
2105    {
2106      array_push($tag_ids, $matches[1]);
2107    }
2108    else
2109    {
2110      // we have to create a new tag
2111      array_push(
2112        $tag_ids,
2113        tag_id_from_tag_name($raw_tag)
2114        );
2115    }
2116  }
2117
2118  return $tag_ids;
2119}
2120
2121/** returns the argument_ids array with new sequenced keys based on related
2122 * names. Sequence is not case sensitive.
2123 * Warning: By definition, this function breaks original keys
2124 */
2125function order_by_name($element_ids,$name)
2126{
2127  $ordered_element_ids = array();
2128  foreach ($element_ids as $k_id => $element_id)
2129  {
2130    $key = strtolower($name[$element_id]) .'-'. $name[$element_id] .'-'. $k_id;
2131    $ordered_element_ids[$key] = $element_id;
2132  }
2133  ksort($ordered_element_ids);
2134  return $ordered_element_ids;
2135}
2136
2137?>
Note: See TracBrowser for help on using the repository browser.