source: branches/2.4/admin/include/functions.php @ 17982

Revision 17982, 55.7 KB checked in by plg, 7 years ago (diff)

merge r17980 from trunk to branch 2.4

little code refactoring over r17424

bug fixed on Edit Photo page when associating with no album.

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