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

Last change on this file since 8413 was 8265, checked in by plg, 13 years ago

feature 2081 added: ability to choose what to do with photos when deleting an
album : no_delete, delete_orphans, force_delete. Backend only.

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