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

Last change on this file since 1116 was 1116, checked in by rub, 19 years ago

[NBM] Step 7: Add functionalities subscribe/unsubscribe:

o reduce length of check_key
o fix bugs
o send mail on subscribe/unsubscribe
o add and used $conf parameters
o review keyword of languages
o improve selection/check
o can subscribe/unsubscribe with a link include on mail
o fix bug mass_update collate

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.6 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | PhpWebGallery - a PHP based picture gallery                           |
4// | Copyright (C) 2002-2003 Pierrick LE GALL - pierrick@phpwebgallery.net |
5// | Copyright (C) 2003-2006 PhpWebGallery Team - http://phpwebgallery.net |
6// +-----------------------------------------------------------------------+
7// | branch        : BSF (Best So Far)
8// | file          : $RCSfile$
9// | last update   : $Date: 2006-04-01 01:14:57 +0000 (Sat, 01 Apr 2006) $
10// | last modifier : $Author: rub $
11// | revision      : $Revision: 1116 $
12// +-----------------------------------------------------------------------+
13// | This program is free software; you can redistribute it and/or modify  |
14// | it under the terms of the GNU General Public License as published by  |
15// | the Free Software Foundation                                          |
16// |                                                                       |
17// | This program is distributed in the hope that it will be useful, but   |
18// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
19// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
20// | General Public License for more details.                              |
21// |                                                                       |
22// | You should have received a copy of the GNU General Public License     |
23// | along with this program; if not, write to the Free Software           |
24// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
25// | USA.                                                                  |
26// +-----------------------------------------------------------------------+
27
28include(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
29
30/**
31 * returns an array with all picture files according to $conf['file_ext']
32 *
33 * @param string $dir
34 * @return array
35 */
36function get_pwg_files($dir)
37{
38  global $conf;
39
40  $pictures = array();
41  if ($opendir = opendir($dir))
42  {
43    while ($file = readdir($opendir))
44    {
45      if (in_array(get_extension($file), $conf['file_ext']))
46      {
47        array_push($pictures, $file);
48      }
49    }
50  }
51  return $pictures;
52}
53
54/**
55 * returns an array with all thumbnails according to $conf['picture_ext']
56 * and $conf['prefix_thumbnail']
57 *
58 * @param string $dir
59 * @return array
60 */
61function get_thumb_files($dir)
62{
63  global $conf;
64
65  $prefix_length = strlen($conf['prefix_thumbnail']);
66
67  $thumbnails = array();
68  if ($opendir = @opendir($dir.'/thumbnail'))
69  {
70    while ($file = readdir($opendir))
71    {
72      if (in_array(get_extension($file), $conf['picture_ext'])
73          and substr($file, 0, $prefix_length) == $conf['prefix_thumbnail'])
74      {
75        array_push($thumbnails, $file);
76      }
77    }
78  }
79  return $thumbnails;
80}
81
82/**
83 * returns an array with representative picture files of a directory
84 * according to $conf['picture_ext']
85 *
86 * @param string $dir
87 * @return array
88 */
89function get_representative_files($dir)
90{
91  global $conf;
92
93  $pictures = array();
94  if ($opendir = @opendir($dir.'/pwg_representative'))
95  {
96    while ($file = readdir($opendir))
97    {
98      if (in_array(get_extension($file), $conf['picture_ext']))
99      {
100        array_push($pictures, $file);
101      }
102    }
103  }
104  return $pictures;
105}
106
107// The function delete_site deletes a site and call the function
108// delete_categories for each primary category of the site
109function delete_site( $id )
110{
111  // destruction of the categories of the site
112  $query = '
113SELECT id
114  FROM '.CATEGORIES_TABLE.'
115  WHERE site_id = '.$id.'
116;';
117  $result = pwg_query($query);
118  $category_ids = array();
119  while ($row = mysql_fetch_array($result))
120  {
121    array_push($category_ids, $row['id']);
122  }
123  delete_categories($category_ids);
124
125  // destruction of the site
126  $query = '
127DELETE FROM '.SITES_TABLE.'
128  WHERE id = '.$id.'
129;';
130  pwg_query($query);
131}
132
133
134// The function delete_categories deletes the categories identified by the
135// (numeric) key of the array $ids. It also deletes (in the database) :
136//    - all the elements of the category (delete_elements, see further)
137//    - all the links between elements and this category
138//    - all the restrictions linked to the category
139// The function works recursively.
140function delete_categories($ids)
141{
142  global $counts;
143
144  if (count($ids) == 0)
145  {
146    return;
147  }
148
149  // add sub-category ids to the given ids : if a category is deleted, all
150  // sub-categories must be so
151  $ids = get_subcat_ids($ids);
152
153  // destruction of all the related elements
154  $query = '
155SELECT image_id
156  FROM '.IMAGE_CATEGORY_TABLE.'
157  WHERE is_storage = \'true\'
158    AND category_id IN ('.
159    wordwrap(
160      implode(', ', $ids),
161      80,
162      "\n"
163      ).
164    ')
165;';
166  $result = pwg_query($query);
167  $element_ids = array();
168  while ($row = mysql_fetch_array($result))
169  {
170    array_push($element_ids, $row['image_id']);
171  }
172  delete_elements($element_ids);
173
174  // destruction of the links between images and this category
175  $query = '
176DELETE FROM '.IMAGE_CATEGORY_TABLE.'
177  WHERE category_id IN (
178'.wordwrap(implode(', ', $ids), 80, "\n").')
179;';
180  pwg_query($query);
181
182  // destruction of the access linked to the category
183  $query = '
184DELETE FROM '.USER_ACCESS_TABLE.'
185  WHERE cat_id IN (
186'.wordwrap(implode(', ', $ids), 80, "\n").')
187;';
188  pwg_query($query);
189
190  $query = '
191DELETE FROM '.GROUP_ACCESS_TABLE.'
192  WHERE cat_id IN (
193'.wordwrap(implode(', ', $ids), 80, "\n").')
194;';
195  pwg_query($query);
196
197  // source/destination links deletion
198  $query = '
199SELECT destination, source
200  FROM '.CATEGORIES_LINK_TABLE.'
201  WHERE source IN ('.implode(',', $ids).')
202    OR destination IN ('.implode(',', $ids).')
203;';
204  $result = pwg_query($query);
205
206  $sources_of = array();
207
208  while ($row = mysql_fetch_array($result))
209  {
210    if (!isset($sources_of[ $row['destination'] ]))
211    {
212      $sources_of[ $row['destination'] ] = array();
213    }
214
215    array_push(
216      $sources_of[ $row['destination'] ],
217      $row['source']
218      );
219  }
220
221  foreach ($sources_of as $destination => $sources)
222  {
223    delete_sources($destination, $sources);
224  }
225
226  update_category();
227
228  // destruction of the category
229  $query = '
230DELETE FROM '.CATEGORIES_TABLE.'
231  WHERE id IN (
232'.wordwrap(implode(', ', $ids), 80, "\n").')
233;';
234  pwg_query($query);
235
236  if (isset($counts['del_categories']))
237  {
238    $counts['del_categories']+= count($ids);
239  }
240}
241
242// The function delete_elements deletes the elements identified by the
243// (numeric) values of the array $ids. It also deletes (in the database) :
244//    - all the comments related to elements
245//    - all the links between categories and elements
246//    - all the favorites associated to elements
247function delete_elements($ids)
248{
249  global $counts;
250
251  if (count($ids) == 0)
252  {
253    return;
254  }
255
256  // destruction of the comments on the image
257  $query = '
258DELETE FROM '.COMMENTS_TABLE.'
259  WHERE image_id IN (
260'.wordwrap(implode(', ', $ids), 80, "\n").')
261;';
262  pwg_query($query);
263
264  // destruction of the links between images and this category
265  $query = '
266DELETE FROM '.IMAGE_CATEGORY_TABLE.'
267  WHERE image_id IN (
268'.wordwrap(implode(', ', $ids), 80, "\n").')
269;';
270  pwg_query($query);
271
272  // destruction of the favorites associated with the picture
273  $query = '
274DELETE FROM '.FAVORITES_TABLE.'
275  WHERE image_id IN (
276'.wordwrap(implode(', ', $ids), 80, "\n").')
277;';
278  pwg_query($query);
279
280  // destruction of the rates associated to this element
281  $query = '
282DELETE FROM '.RATE_TABLE.'
283  WHERE element_id IN (
284'.wordwrap(implode(', ', $ids), 80, "\n").')
285;';
286  pwg_query($query);
287
288  // destruction of the rates associated to this element
289  $query = '
290DELETE FROM '.CADDIE_TABLE.'
291  WHERE element_id IN (
292'.wordwrap(implode(', ', $ids), 80, "\n").')
293;';
294  pwg_query($query);
295
296  // destruction of the image
297  $query = '
298DELETE FROM '.IMAGES_TABLE.'
299  WHERE id IN (
300'.wordwrap(implode(', ', $ids), 80, "\n").')
301;';
302  pwg_query($query);
303
304  if (isset($counts['del_elements']))
305  {
306    $counts['del_elements']+= count($ids);
307  }
308}
309
310// The delete_user function delete a user identified by the $user_id
311// It also deletes :
312//     - all the access linked to this user
313//     - all the links to any group
314//     - all the favorites linked to this user
315//     - calculated permissions linked to the user
316//     - all datas about notifications for the user
317function delete_user($user_id)
318{
319  global $conf;
320
321  // destruction of the access linked to the user
322  $query = '
323DELETE FROM '.USER_ACCESS_TABLE.'
324  WHERE user_id = '.$user_id.'
325;';
326  pwg_query($query);
327
328  // destruction of data notification by mail for this user
329  $query = '
330DELETE FROM '.USER_MAIL_NOTIFICATION_TABLE.'
331  WHERE user_id = '.$user_id.'
332;';
333  pwg_query($query);
334
335  // destruction of data RSS notification for this user
336  $query = '
337DELETE FROM '.USER_FEED_TABLE.'
338  WHERE user_id = '.$user_id.'
339;';
340  pwg_query($query);
341
342  // destruction of the group links for this user
343  $query = '
344DELETE FROM '.USER_GROUP_TABLE.'
345  WHERE user_id = '.$user_id.'
346;';
347  pwg_query($query);
348
349  // destruction of the favorites associated with the user
350  $query = '
351DELETE FROM '.FAVORITES_TABLE.'
352  WHERE user_id = '.$user_id.'
353;';
354  pwg_query($query);
355
356  // deletion of calculated permissions linked to the user
357  $query = '
358DELETE FROM '.USER_CACHE_TABLE.'
359  WHERE user_id = '.$user_id.'
360;';
361  pwg_query($query);
362
363  // deletion of phpwebgallery specific informations
364  $query = '
365DELETE FROM '.USER_INFOS_TABLE.'
366  WHERE user_id = '.$user_id.'
367;';
368  pwg_query($query);
369
370  // destruction of the user
371  $query = '
372DELETE FROM '.USERS_TABLE.'
373  WHERE '.$conf['user_fields']['id'].' = '.$user_id.'
374;';
375  pwg_query($query);
376}
377
378/**
379 * updates calculated informations about a set of categories : date_last and
380 * nb_images. It also verifies that the representative picture is really
381 * linked to the category. Optionnaly recursive.
382 *
383 * @param mixed category id
384 * @param boolean recursive
385 * @returns void
386 */
387function update_category($ids = 'all', $recursive = false)
388{
389  global $conf;
390
391  // retrieving all categories to update
392  $cat_ids = array();
393
394  $query = '
395SELECT id
396  FROM '.CATEGORIES_TABLE;
397  if (is_array($ids))
398  {
399    if ($recursive)
400    {
401      foreach ($ids as $num => $id)
402      {
403        if ($num == 0)
404        {
405          $query.= '
406  WHERE ';
407        }
408        else
409        {
410          $query.= '
411  OR    ';
412        }
413        $query.= 'uppercats REGEXP \'(^|,)'.$id.'(,|$)\'';
414      }
415    }
416    else
417    {
418      $query.= '
419  WHERE id IN ('.wordwrap(implode(', ', $ids), 80, "\n").')';
420    }
421  }
422  $query.= '
423;';
424  $cat_ids = array_unique(array_from_query($query, 'id'));
425
426  if (count($cat_ids) == 0)
427  {
428    return false;
429  }
430
431  // calculate informations about categories retrieved
432  $query = '
433SELECT category_id,
434       COUNT(image_id) AS nb_images,
435       MAX(date_available) AS date_last
436  FROM '.IMAGES_TABLE.'
437    INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id
438  WHERE category_id IN ('.wordwrap(implode(', ', $cat_ids), 80, "\n").')
439  GROUP BY category_id
440;';
441  $result = pwg_query($query);
442  $datas = array();
443  $query_ids = array();
444  while ($row = mysql_fetch_array($result))
445  {
446    array_push($query_ids, $row['category_id']);
447
448    array_push(
449      $datas,
450      array(
451        'id'        => $row['category_id'],
452        'date_last' => $row['date_last'],
453        'nb_images' => $row['nb_images']
454        )
455      );
456  }
457  // if all links between a category and elements have disappeared, no line
458  // is returned but the update must be done !
459  foreach (array_diff($cat_ids, $query_ids) as $id)
460  {
461    array_push(
462      $datas,
463      array(
464        'id'        => $id,
465        'nb_images' => 0,
466        )
467      );
468  }
469
470  mass_updates(
471    CATEGORIES_TABLE,
472    array(
473      'primary' => array('id'),
474      'update'  => array('date_last', 'nb_images')
475      ),
476    $datas
477    );
478
479  // representative pictures
480  if (count($cat_ids) > 0)
481  {
482    // find all categories where the setted representative is not possible :
483    // the picture does not exist
484    $query = '
485SELECT c.id
486  FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i
487    ON c.representative_picture_id = i.id
488  WHERE representative_picture_id IS NOT NULL
489    AND c.id IN ('.wordwrap(implode(', ', $cat_ids), 80, "\n").')
490    AND i.id IS NULL
491;';
492    $wrong_representant = array_from_query($query, 'id');
493
494    if ($conf['allow_random_representative'])
495    {
496      if (count($wrong_representant) > 0)
497      {
498        $query = '
499UPDATE '.CATEGORIES_TABLE.'
500  SET representative_picture_id = NULL
501  WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 80, "\n").')
502;';
503        pwg_query($query);
504      }
505    }
506    else
507    {
508      $to_null = array();
509      $to_rand = array();
510
511      if (count($wrong_representant) > 0)
512      {
513        // among the categories with an unknown representant, we dissociate
514        // categories containing pictures and categories containing no
515        // pictures. Indeed, the representant must set to NULL if no picture
516        // in the category and set to a random picture otherwise.
517        $query = '
518SELECT id
519  FROM '.CATEGORIES_TABLE.'
520  WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 80, "\n").')
521    AND nb_images = 0
522;';
523        $to_null = array_from_query($query, 'id');
524        $to_rand = array_diff($wrong_representant, $to_null);
525      }
526
527      if (count($to_null) > 0)
528      {
529        $query = '
530UPDATE '.CATEGORIES_TABLE.'
531  SET representative_picture_id = NULL
532  WHERE id IN ('.wordwrap(implode(', ', $to_null), 80, "\n").')
533;';
534        pwg_query($query);
535      }
536
537      // If the random representant is not allowed, we need to find
538      // categories with elements and with no representant. Those categories
539      // must be added to the list of categories to set to a random
540      // representant.
541      $query = '
542SELECT id
543  FROM '.CATEGORIES_TABLE.'
544  WHERE representative_picture_id IS NULL
545    AND nb_images != 0
546    AND id IN ('.wordwrap(implode(', ', $cat_ids), 80, "\n").')
547;';
548      $to_rand =
549        array_unique(
550          array_merge(
551            $to_rand,
552            array_from_query($query, 'id')
553            )
554          );
555
556      if (count($to_rand) > 0)
557      {
558        set_random_representant($to_rand);
559      }
560    }
561  }
562}
563
564function date_convert_back( $date )
565{
566  // date arrives at this format : YYYY-MM-DD
567  // It must be transformed in DD/MM/YYYY
568  if ( $date != '' )
569  {
570    list($year,$month,$day) = explode( '-', $date );
571    return $day.'/'.$month.'/'.$year;
572  }
573  else
574  {
575    return '';
576  }
577}
578
579/**
580 * returns an array with relevant keywords found in the given string.
581 *
582 * Keywords must be separated by comma or space characters.
583 *
584 * @param string keywords_string
585 * @return array
586 */
587function get_keywords($keywords_string)
588{
589  return
590    array_unique(
591      preg_split(
592        '/[\s,]+/',
593        $keywords_string
594        )
595      );
596}
597
598/**
599 * returns an array containing sub-directories which can be a category,
600 * recursive by default
601 *
602 * directories nammed "thumbnail", "pwg_high" or "pwg_representative" are
603 * omitted
604 *
605 * @param string $basedir
606 * @return array
607 */
608function get_fs_directories($path, $recursive = true)
609{
610  $dirs = array();
611
612  if (is_dir($path))
613  {
614    if ($contents = opendir($path))
615    {
616      while (($node = readdir($contents)) !== false)
617      {
618        if (is_dir($path.'/'.$node)
619            and $node != '.'
620            and $node != '..'
621            and $node != '.svn'
622            and $node != 'thumbnail'
623            and $node != 'pwg_high'
624            and $node != 'pwg_representative')
625        {
626          array_push($dirs, $path.'/'.$node);
627          if ($recursive)
628          {
629            $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node));
630          }
631        }
632      }
633    }
634  }
635
636  return $dirs;
637}
638
639/**
640 * inserts multiple lines in a table
641 *
642 * @param string table_name
643 * @param array dbfields
644 * @param array inserts
645 * @return void
646 */
647function mass_inserts($table_name, $dbfields, $datas)
648{
649  // inserts all found categories
650  $query = '
651INSERT INTO '.$table_name.'
652  ('.implode(',', $dbfields).')
653   VALUES';
654  foreach ($datas as $insert_id => $insert)
655  {
656    $query.= '
657  ';
658    if ($insert_id > 0)
659    {
660      $query.= ',';
661    }
662    $query.= '(';
663    foreach ($dbfields as $field_id => $dbfield)
664    {
665      if ($field_id > 0)
666      {
667        $query.= ',';
668      }
669
670      if (!isset($insert[$dbfield]) or $insert[$dbfield] == '')
671      {
672        $query.= 'NULL';
673      }
674      else
675      {
676        $query.= "'".$insert[$dbfield]."'";
677      }
678    }
679    $query.=')';
680  }
681  $query.= '
682;';
683  pwg_query($query);
684}
685
686/**
687 * updates multiple lines in a table
688 *
689 * @param string table_name
690 * @param array dbfields
691 * @param array datas
692 * @return void
693 */
694function mass_updates($tablename, $dbfields, $datas)
695{
696  // depending on the MySQL version, we use the multi table update or N
697  // update queries
698  $query = 'SELECT VERSION() AS version;';
699  list($mysql_version) = mysql_fetch_array(pwg_query($query));
700  if (count($datas) < 10 or version_compare($mysql_version, '4.0.4') < 0)
701  {
702    // MySQL is prior to version 4.0.4, multi table update feature is not
703    // available
704    foreach ($datas as $data)
705    {
706      $query = '
707UPDATE '.$tablename.'
708  SET ';
709      $is_first = true;
710      foreach ($dbfields['update'] as $num => $key)
711      {
712        if (!$is_first)
713        {
714          $query.= ",\n      ";
715        }
716        $query.= $key.' = ';
717        if (isset($data[$key]) and $data[$key] != '')
718        {
719          $query.= '\''.$data[$key].'\'';
720        }
721        else
722        {
723          $query.= 'NULL';
724        }
725        $is_first = false;
726      }
727      $query.= '
728  WHERE ';
729      foreach ($dbfields['primary'] as $num => $key)
730      {
731        if ($num > 1)
732        {
733          $query.= ' AND ';
734        }
735        $query.= $key.' = \''.$data[$key].'\'';
736      }
737      $query.= '
738;';
739      pwg_query($query);
740    }
741  }
742  else
743  {
744    // creation of the temporary table
745    $query = '
746SHOW FULL COLUMNS FROM '.$tablename.'
747;';
748    $result = pwg_query($query);
749    $columns = array();
750    $all_fields = array_merge($dbfields['primary'], $dbfields['update']);
751    while ($row = mysql_fetch_array($result))
752    {
753      if (in_array($row['Field'], $all_fields))
754      {
755        $column = $row['Field'];
756        $column.= ' '.$row['Type'];
757        if (!isset($row['Null']) or $row['Null'] == '')
758        {
759          $column.= ' NOT NULL';
760        }
761        if (isset($row['Default']))
762        {
763          $column.= " default '".$row['Default']."'";
764        }
765        if (isset($row['Collation']) and $row['Collation'] != 'NULL')
766        {
767          $column.= " collate '".$row['Collation']."'";
768        }
769        array_push($columns, $column);
770      }
771    }
772
773    $temporary_tablename = $tablename.'_'.micro_seconds();
774
775    $query = '
776CREATE TABLE '.$temporary_tablename.'
777(
778'.implode(",\n", $columns).',
779PRIMARY KEY ('.implode(',', $dbfields['primary']).')
780)
781;';
782    pwg_query($query);
783    mass_inserts($temporary_tablename, $all_fields, $datas);
784    // update of images table by joining with temporary table
785    $query = '
786UPDATE '.$tablename.' AS t1, '.$temporary_tablename.' AS t2
787  SET '.
788      implode(
789        "\n    , ",
790        array_map(
791          create_function('$s', 'return "t1.$s = t2.$s";'),
792          $dbfields['update']
793          )
794        ).'
795  WHERE '.
796      implode(
797        "\n    AND ",
798        array_map(
799          create_function('$s', 'return "t1.$s = t2.$s";'),
800          $dbfields['primary']
801          )
802        ).'
803;';
804    pwg_query($query);
805    $query = '
806DROP TABLE '.$temporary_tablename.'
807;';
808    pwg_query($query);
809  }
810}
811
812/**
813 * updates the global_rank of categories under the given id_uppercat
814 *
815 * @param int id_uppercat
816 * @return void
817 */
818function update_global_rank($id_uppercat = 'all')
819{
820  $query = '
821SELECT id,rank
822  FROM '.CATEGORIES_TABLE.'
823;';
824  $result = pwg_query($query);
825  $ranks_array = array();
826  while ($row = mysql_fetch_array($result))
827  {
828    $ranks_array[$row['id']] = $row['rank'];
829  }
830
831  // which categories to update ?
832  $uppercats_array = array();
833
834  $query = '
835SELECT id,uppercats
836  FROM '.CATEGORIES_TABLE;
837  if (is_numeric($id_uppercat))
838  {
839    $query.= '
840  WHERE uppercats REGEXP \'(^|,)'.$id_uppercat.'(,|$)\'
841    AND id != '.$id_uppercat.'
842';
843  }
844  $query.= '
845;';
846  $result = pwg_query($query);
847  while ($row = mysql_fetch_array($result))
848  {
849    $uppercats_array[$row['id']] =  $row['uppercats'];
850  }
851
852  $datas = array();
853  foreach ($uppercats_array as $id => $uppercats)
854  {
855    array_push(
856      $datas,
857      array(
858        'id'          => $id,
859        'global_rank' => preg_replace(
860          '/(\d+)/e',
861          "\$ranks_array['$1']",
862          str_replace(',', '.', $uppercats)
863          ),
864        )
865      );
866  }
867
868  mass_updates(
869    CATEGORIES_TABLE,
870    array(
871      'primary' => array('id'),
872      'update'  => array('global_rank')
873      ),
874    $datas
875    );
876}
877
878/**
879 * change the visible property on a set of categories
880 *
881 * @param array categories
882 * @param string value
883 * @return void
884 */
885function set_cat_visible($categories, $value)
886{
887  if (!in_array($value, array('true', 'false')))
888  {
889    return false;
890  }
891
892  // unlocking a category => all its parent categories become unlocked
893  if ($value == 'true')
894  {
895    $uppercats = get_uppercat_ids($categories);
896    $query = '
897UPDATE '.CATEGORIES_TABLE.'
898  SET visible = \'true\'
899  WHERE id IN ('.implode(',', $uppercats).')
900;';
901    pwg_query($query);
902  }
903  // locking a category   => all its child categories become locked
904  if ($value == 'false')
905  {
906    $subcats = get_subcat_ids($categories);
907    $query = '
908UPDATE '.CATEGORIES_TABLE.'
909  SET visible = \'false\'
910  WHERE id IN ('.implode(',', $subcats).')
911;';
912    pwg_query($query);
913  }
914}
915
916/**
917 * change the status property on a set of categories : private or public
918 *
919 * @param array categories
920 * @param string value
921 * @return void
922 */
923function set_cat_status($categories, $value)
924{
925  if (!in_array($value, array('public', 'private')))
926  {
927    return false;
928  }
929
930  // make public a category => all its parent categories become public
931  if ($value == 'public')
932  {
933    $uppercats = get_uppercat_ids($categories);
934    $query = '
935UPDATE '.CATEGORIES_TABLE.'
936  SET status = \'public\'
937  WHERE id IN ('.implode(',', $uppercats).')
938;';
939    pwg_query($query);
940  }
941  // make a category private => all its child categories become private
942  if ($value == 'private')
943  {
944    $subcats = get_subcat_ids($categories);
945    $query = '
946UPDATE '.CATEGORIES_TABLE.'
947  SET status = \'private\'
948  WHERE id IN ('.implode(',', $subcats).')
949;';
950    pwg_query($query);
951  }
952}
953
954/**
955 * returns all uppercats category ids of the given category ids
956 *
957 * @param array cat_ids
958 * @return array
959 */
960function get_uppercat_ids($cat_ids)
961{
962  if (!is_array($cat_ids) or count($cat_ids) < 1)
963  {
964    return array();
965  }
966
967  $uppercats = array();
968
969  $query = '
970SELECT uppercats
971  FROM '.CATEGORIES_TABLE.'
972  WHERE id IN ('.implode(',', $cat_ids).')
973;';
974  $result = pwg_query($query);
975  while ($row = mysql_fetch_array($result))
976  {
977    $uppercats = array_merge($uppercats,
978                             explode(',', $row['uppercats']));
979  }
980  $uppercats = array_unique($uppercats);
981
982  return $uppercats;
983}
984
985/**
986 * set a new random representant to the categories
987 *
988 * @param array categories
989 */
990function set_random_representant($categories)
991{
992  $datas = array();
993  foreach ($categories as $category_id)
994  {
995    $query = '
996SELECT image_id
997  FROM '.IMAGE_CATEGORY_TABLE.'
998  WHERE category_id = '.$category_id.'
999  ORDER BY RAND()
1000  LIMIT 0,1
1001;';
1002    list($representative) = mysql_fetch_array(pwg_query($query));
1003    $data = array('id' => $category_id,
1004                  'representative_picture_id' => $representative);
1005    array_push($datas, $data);
1006  }
1007
1008  $fields = array('primary' => array('id'),
1009                  'update' => array('representative_picture_id'));
1010  mass_updates(CATEGORIES_TABLE, $fields, $datas);
1011}
1012
1013/**
1014 * order categories (update categories.rank and global_rank database fields)
1015 *
1016 * the purpose of this function is to give a rank for all categories
1017 * (insides its sub-category), even the newer that have none at te
1018 * beginning. For this, ordering function selects all categories ordered by
1019 * rank ASC then name ASC for each uppercat.
1020 *
1021 * @returns void
1022 */
1023function ordering()
1024{
1025  $current_rank = 0;
1026  $current_uppercat = '';
1027
1028  $query = '
1029SELECT id, if(id_uppercat is null,\'\',id_uppercat) AS id_uppercat
1030  FROM '.CATEGORIES_TABLE.'
1031  ORDER BY id_uppercat,rank,name
1032;';
1033  $result = pwg_query($query);
1034  $datas = array();
1035  while ($row = mysql_fetch_array($result))
1036  {
1037    if ($row['id_uppercat'] != $current_uppercat)
1038    {
1039      $current_rank = 0;
1040      $current_uppercat = $row['id_uppercat'];
1041    }
1042    $data = array('id' => $row['id'], 'rank' => ++$current_rank);
1043    array_push($datas, $data);
1044  }
1045
1046  $fields = array('primary' => array('id'), 'update' => array('rank'));
1047  mass_updates(CATEGORIES_TABLE, $fields, $datas);
1048}
1049
1050/**
1051 * returns the fulldir for each given category id
1052 *
1053 * @param array cat_ids
1054 * @return array
1055 */
1056function get_fulldirs($cat_ids)
1057{
1058  if (count($cat_ids) == 0)
1059  {
1060    return array();
1061  }
1062
1063  // caching directories of existing categories
1064  $query = '
1065SELECT id, dir
1066  FROM '.CATEGORIES_TABLE.'
1067  WHERE dir IS NOT NULL
1068;';
1069  $result = pwg_query($query);
1070  $cat_dirs = array();
1071  while ($row = mysql_fetch_array($result))
1072  {
1073    $cat_dirs[$row['id']] = $row['dir'];
1074  }
1075
1076  // caching galleries_url
1077  $query = '
1078SELECT id, galleries_url
1079  FROM '.SITES_TABLE.'
1080;';
1081  $result = pwg_query($query);
1082  $galleries_url = array();
1083  while ($row = mysql_fetch_array($result))
1084  {
1085    $galleries_url[$row['id']] = $row['galleries_url'];
1086  }
1087
1088  // categories : id, site_id, uppercats
1089  $categories = array();
1090
1091  $query = '
1092SELECT id, uppercats, site_id
1093  FROM '.CATEGORIES_TABLE.'
1094  WHERE id IN (
1095'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
1096;';
1097  $result = pwg_query($query);
1098  while ($row = mysql_fetch_array($result))
1099  {
1100    array_push($categories, $row);
1101  }
1102
1103  // filling $cat_fulldirs
1104  $cat_fulldirs = array();
1105  foreach ($categories as $category)
1106  {
1107    $uppercats = str_replace(',', '/', $category['uppercats']);
1108    $cat_fulldirs[$category['id']] = $galleries_url[$category['site_id']];
1109    $cat_fulldirs[$category['id']].= preg_replace('/(\d+)/e',
1110                                                  "\$cat_dirs['$1']",
1111                                                  $uppercats);
1112  }
1113
1114  return $cat_fulldirs;
1115}
1116
1117/**
1118 * returns an array with all file system files according to
1119 * $conf['file_ext']
1120 *
1121 * @param string $path
1122 * @param bool recursive
1123 * @return array
1124 */
1125function get_fs($path, $recursive = true)
1126{
1127  global $conf;
1128
1129  // because isset is faster than in_array...
1130  if (!isset($conf['flip_picture_ext']))
1131  {
1132    $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
1133  }
1134  if (!isset($conf['flip_file_ext']))
1135  {
1136    $conf['flip_file_ext'] = array_flip($conf['file_ext']);
1137  }
1138
1139  $fs['elements'] = array();
1140  $fs['thumbnails'] = array();
1141  $fs['representatives'] = array();
1142  $subdirs = array();
1143
1144  if (is_dir($path))
1145  {
1146    if ($contents = opendir($path))
1147    {
1148      while (($node = readdir($contents)) !== false)
1149      {
1150        if (is_file($path.'/'.$node))
1151        {
1152          $extension = get_extension($node);
1153
1154//          if (in_array($extension, $conf['picture_ext']))
1155          if (isset($conf['flip_picture_ext'][$extension]))
1156          {
1157            if (basename($path) == 'thumbnail')
1158            {
1159              array_push($fs['thumbnails'], $path.'/'.$node);
1160            }
1161            else if (basename($path) == 'pwg_representative')
1162            {
1163              array_push($fs['representatives'], $path.'/'.$node);
1164            }
1165            else
1166            {
1167              array_push($fs['elements'], $path.'/'.$node);
1168            }
1169          }
1170//          else if (in_array($extension, $conf['file_ext']))
1171          else if (isset($conf['flip_file_ext'][$extension]))
1172          {
1173            array_push($fs['elements'], $path.'/'.$node);
1174          }
1175        }
1176        else if (is_dir($path.'/'.$node)
1177                 and $node != '.'
1178                 and $node != '..'
1179                 and $node != 'pwg_high'
1180                 and $recursive)
1181        {
1182          array_push($subdirs, $node);
1183        }
1184      }
1185    }
1186    closedir($contents);
1187
1188    foreach ($subdirs as $subdir)
1189    {
1190      $tmp_fs = get_fs($path.'/'.$subdir);
1191
1192      $fs['elements']        = array_merge($fs['elements'],
1193                                           $tmp_fs['elements']);
1194
1195      $fs['thumbnails']      = array_merge($fs['thumbnails'],
1196                                           $tmp_fs['thumbnails']);
1197
1198      $fs['representatives'] = array_merge($fs['representatives'],
1199                                           $tmp_fs['representatives']);
1200    }
1201  }
1202  return $fs;
1203}
1204
1205/**
1206 * stupidly returns the current microsecond since Unix epoch
1207 */
1208function micro_seconds()
1209{
1210  $t1 = explode(' ', microtime());
1211  $t2 = explode('.', $t1[0]);
1212  $t2 = $t1[1].substr($t2[1], 0, 6);
1213  return $t2;
1214}
1215
1216/**
1217 * synchronize base users list and related users list
1218 *
1219 * compares and synchronizes base users table (USERS_TABLE) with its child
1220 * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each
1221 * base user must be present in child tables, users in child tables not
1222 * present in base table must be deleted.
1223 *
1224 * @return void
1225 */
1226function sync_users()
1227{
1228  global $conf;
1229
1230  $query = '
1231SELECT '.$conf['user_fields']['id'].' AS id
1232  FROM '.USERS_TABLE.'
1233;';
1234  $base_users = array_from_query($query, 'id');
1235
1236  $query = '
1237SELECT user_id
1238  FROM '.USER_INFOS_TABLE.'
1239;';
1240  $infos_users = array_from_query($query, 'user_id');
1241
1242  // users present in $base_users and not in $infos_users must be added
1243  $to_create = array_diff($base_users, $infos_users);
1244
1245  if (count($to_create) > 0)
1246  {
1247    $inserts = array();
1248
1249    list($dbnow) = mysql_fetch_row(pwg_query('SELECT NOW();'));
1250
1251    foreach ($to_create as $user_id)
1252    {
1253      $insert = array();
1254      $insert['user_id'] = $user_id;
1255      $insert['status'] = 'normal';
1256      $insert['template'] = $conf['default_template'];
1257      $insert['nb_image_line'] = $conf['nb_image_line'];
1258      $insert['nb_line_page'] = $conf['nb_line_page'];
1259      $insert['language'] = $conf['default_language'];
1260      $insert['recent_period'] = $conf['recent_period'];
1261      $insert['expand'] = boolean_to_string($conf['auto_expand']);
1262      $insert['show_nb_comments'] =
1263        boolean_to_string($conf['show_nb_comments']);
1264      $insert['maxwidth'] = $conf['default_maxwidth'];
1265      $insert['maxheight'] = $conf['default_maxheight'];
1266      $insert['registration_date'] = $dbnow;
1267
1268      array_push($inserts, $insert);
1269    }
1270
1271    mass_inserts(USER_INFOS_TABLE,
1272                 array_keys($inserts[0]),
1273                 $inserts);
1274  }
1275
1276  // users present in user related tables must be present in the base user
1277  // table
1278  $tables = array(
1279    USER_MAIL_NOTIFICATION_TABLE,
1280    USER_FEED_TABLE,
1281    USER_INFOS_TABLE,
1282    USER_ACCESS_TABLE,
1283    USER_CACHE_TABLE,
1284    USER_GROUP_TABLE
1285    );
1286 
1287  foreach ($tables as $table)
1288  {
1289    $query = '
1290SELECT user_id
1291  FROM '.$table.'
1292;';
1293    $to_delete = array_diff(
1294      array_from_query($query, 'user_id'),
1295      $base_users
1296      );
1297
1298    if (count($to_delete) > 0)
1299    {
1300      $query = '
1301DELETE
1302  FROM '.$table.'
1303  WHERE user_id in ('.implode(',', $to_delete).')
1304;';
1305      pwg_query($query);
1306    }
1307  }
1308}
1309
1310/**
1311 * updates categories.uppercats field based on categories.id +
1312 * categories.id_uppercat
1313 *
1314 * @return void
1315 */
1316function update_uppercats()
1317{
1318  $uppercat_ids = array();
1319
1320  $query = '
1321SELECT id, id_uppercat
1322  FROM '.CATEGORIES_TABLE.'
1323;';
1324  $result = pwg_query($query);
1325  while ($row = mysql_fetch_array($result))
1326  {
1327    $uppercat_ids[$row['id']] =
1328      !empty($row['id_uppercat']) ? $row['id_uppercat'] : 'NULL';
1329  }
1330
1331  // uppercats array associates a category id to the list of uppercats id.
1332  $uppercats = array();
1333
1334  foreach (array_keys($uppercat_ids) as $id)
1335  {
1336    $uppercats[$id] = array();
1337
1338    $uppercat = $id;
1339
1340    while ($uppercat != 'NULL')
1341    {
1342      array_push($uppercats[$id], $uppercat);
1343      $uppercat = $uppercat_ids[$uppercat];
1344    }
1345  }
1346
1347  $datas = array();
1348
1349  foreach ($uppercats as $id => $list)
1350  {
1351    array_push(
1352      $datas,
1353      array(
1354        'id' => $id,
1355        'uppercats' => implode(',', array_reverse($list))
1356        )
1357      );
1358  }
1359
1360  $fields = array('primary' => array('id'), 'update' => array('uppercats'));
1361  mass_updates(CATEGORIES_TABLE, $fields, $datas);
1362}
1363
1364/**
1365 * update images.path field
1366 *
1367 * @return void
1368 */
1369function update_path()
1370{
1371  $images_of = array();
1372
1373  $query = '
1374SELECT category_id, image_id
1375  FROM '.IMAGE_CATEGORY_TABLE.'
1376  WHERE is_storage = \'true\'
1377;';
1378  $result = pwg_query($query);
1379  while ($row = mysql_fetch_array($result))
1380  {
1381    if (!isset($images_of[ $row['category_id'] ]))
1382    {
1383      $images_of[ $row['category_id'] ] = array();
1384    }
1385
1386    array_push(
1387      $images_of[ $row['category_id'] ],
1388      $row['image_id']
1389      );
1390  }
1391
1392  $fulldirs = get_fulldirs(
1393    array_keys($images_of)
1394    );
1395
1396  foreach ($images_of as $cat_id => $image_ids)
1397  {
1398    $query = '
1399UPDATE '.IMAGES_TABLE.'
1400  SET path = CONCAT(\''.$fulldirs[$cat_id].'\',\'/\',file)
1401  WHERE id IN ('.
1402      wordwrap(
1403        implode(', ', $image_ids),
1404        80,
1405        "\n").
1406      ')
1407;';
1408    pwg_query($query);
1409  }
1410}
1411
1412/**
1413 * update images.average_rate field
1414 * param int $element_id optional, otherwise applies to all
1415 * @return void
1416 */
1417function update_average_rate( $element_id=-1 )
1418{
1419  $query = '
1420SELECT element_id,
1421       ROUND(AVG(rate),2) AS average_rate
1422  FROM '.RATE_TABLE;
1423  if ( $element_id != -1 )
1424  {
1425    $query .= ' WHERE element_id=' . $element_id;
1426  }
1427  $query .= ' GROUP BY element_id;';
1428
1429  $result = pwg_query($query);
1430
1431  $datas = array();
1432
1433  while ($row = mysql_fetch_array($result))
1434  {
1435    array_push(
1436      $datas,
1437      array(
1438        'id' => $row['element_id'],
1439        'average_rate' => $row['average_rate']
1440        )
1441      );
1442  }
1443
1444  mass_updates(
1445    IMAGES_TABLE,
1446    array(
1447      'primary' => array('id'),
1448      'update' => array('average_rate')
1449      ),
1450    $datas
1451    );
1452
1453  $query='
1454SELECT id FROM '.IMAGES_TABLE .'
1455  LEFT JOIN '.RATE_TABLE.' ON id=element_id
1456  WHERE element_id IS NULL AND average_rate IS NOT NULL';
1457  if ( $element_id != -1 )
1458  {
1459    $query .= ' AND id=' . $element_id;
1460  }
1461  $to_update = array_from_query( $query, 'id');
1462
1463  if ( !empty($to_update) )
1464  {
1465    $query='
1466UPDATE '.IMAGES_TABLE .'
1467  SET average_rate=NULL
1468  WHERE id IN (' . implode(',',$to_update) . ')';
1469    pwg_query($query);
1470  }
1471}
1472
1473/**
1474 * change the parent category of the given categories. The categories are
1475 * supposed virtual.
1476 *
1477 * @param array category identifiers
1478 * @param int parent category identifier
1479 * @return void
1480 */
1481function move_categories($category_ids, $new_parent = -1)
1482{
1483  global $page;
1484
1485  if (count($category_ids) == 0)
1486  {
1487    return;
1488  }
1489
1490  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
1491
1492  $categories = array();
1493
1494  $query = '
1495SELECT id, id_uppercat, status, uppercats
1496  FROM '.CATEGORIES_TABLE.'
1497  WHERE id IN ('.implode(',', $category_ids).')
1498;';
1499  $result = pwg_query($query);
1500  while ($row = mysql_fetch_array($result))
1501  {
1502    $categories[$row['id']] =
1503      array(
1504        'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
1505        'status' => $row['status'],
1506        'uppercats' => $row['uppercats']
1507        );
1508  }
1509
1510  // is the movement possible? The movement is impossible if you try to move
1511  // a category in a sub-category or itself
1512  if ('NULL' != $new_parent)
1513  {
1514    $query = '
1515SELECT uppercats
1516  FROM '.CATEGORIES_TABLE.'
1517  WHERE id = '.$new_parent.'
1518;';
1519    list($new_parent_uppercats) = mysql_fetch_row(pwg_query($query));
1520
1521    foreach ($categories as $category)
1522    {
1523      // technically, you can't move a category with uppercats 12,125,13,14
1524      // into a new parent category with uppercats 12,125,13,14,24
1525      if (preg_match('/^'.$category['uppercats'].'/', $new_parent_uppercats))
1526      {
1527        array_push(
1528          $page['errors'],
1529          l10n('You cannot move a category in its own sub category')
1530          );
1531        return;
1532      }
1533    }
1534  }
1535
1536  $tables =
1537    array(
1538      USER_ACCESS_TABLE => 'user_id',
1539      GROUP_ACCESS_TABLE => 'group_id'
1540      );
1541
1542  $query = '
1543UPDATE '.CATEGORIES_TABLE.'
1544  SET id_uppercat = '.$new_parent.'
1545  WHERE id IN ('.implode(',', $category_ids).')
1546;';
1547  pwg_query($query);
1548
1549  update_uppercats();
1550  ordering();
1551  update_global_rank();
1552
1553  // status and related permissions management
1554  if ('NULL' == $new_parent)
1555  {
1556    $parent_status = 'public';
1557  }
1558  else
1559  {
1560    $query = '
1561SELECT status
1562  FROM '.CATEGORIES_TABLE.'
1563  WHERE id = '.$new_parent.'
1564;';
1565    list($parent_status) = mysql_fetch_row(pwg_query($query));
1566  }
1567
1568  if ('private' == $parent_status)
1569  {
1570    foreach ($categories as $cat_id => $category)
1571    {
1572      switch ($category['status'])
1573      {
1574        case 'public' :
1575        {
1576          set_cat_status(array($cat_id), 'private');
1577          break;
1578        }
1579        case 'private' :
1580        {
1581          $subcats = get_subcat_ids(array($cat_id));
1582
1583          foreach ($tables as $table => $field)
1584          {
1585            $query = '
1586SELECT '.$field.'
1587  FROM '.$table.'
1588  WHERE cat_id = '.$cat_id.'
1589;';
1590            $category_access = array_from_query($query, $field);
1591
1592            $query = '
1593SELECT '.$field.'
1594  FROM '.$table.'
1595  WHERE cat_id = '.$new_parent.'
1596;';
1597            $parent_access = array_from_query($query, $field);
1598
1599            $to_delete = array_diff($parent_access, $category_access);
1600
1601            if (count($to_delete) > 0)
1602            {
1603              $query = '
1604DELETE FROM '.$table.'
1605  WHERE '.$field.' IN ('.implode(',', $to_delete).')
1606    AND cat_id IN ('.implode(',', $subcats).')
1607;';
1608              pwg_query($query);
1609            }
1610          }
1611          break;
1612        }
1613      }
1614    }
1615  }
1616
1617  array_push(
1618    $page['infos'],
1619    sprintf(
1620      l10n('%d categories moved'),
1621      count($categories)
1622      )
1623    );
1624}
1625
1626/**
1627 * Returns all destinations of a list of source categories. This function
1628 * solves transitivity.
1629 *
1630 * @param mixed array of category ids, empty for all categories
1631 */
1632function get_destinations($categories = 'all')
1633{
1634  $query = '
1635SELECT source, destination
1636  FROM '.CATEGORIES_LINK_TABLE.'
1637';
1638  $result = pwg_query($query);
1639
1640  $destinations_of = array();
1641
1642  while ($row = mysql_fetch_array($result))
1643  {
1644    if (!isset($destinations_of[ $row['source'] ]))
1645    {
1646      $destinations_of[ $row['source'] ] = array();
1647    }
1648
1649    array_push(
1650      $destinations_of[ $row['source'] ],
1651      $row['destination']
1652      );
1653  }
1654
1655  // transitivity resolution: if " => " means "source of", if A=>B=>C
1656  // implies A=>B and A=>C. So A has 2 destinations: B and C.
1657  do
1658  {
1659    // let's suppose we only need a single turn
1660    $need_new_turn = false;
1661
1662    foreach ($destinations_of as $source => $destinations)
1663    {
1664      foreach ($destinations as $destination)
1665      {
1666        // does the current destination has destinations itself?
1667        if (isset($destinations_of[$destination]))
1668        {
1669          // are there destinations of current destination not already among
1670          // destinations of the current source? (advise: take a piece of
1671          // paper and draw a schema). The source itself must not be counted
1672          // as a destination, thus avoiding cyclic links.
1673          $missing_destinations = array_diff(
1674            $destinations_of[$destination],
1675            $destinations,
1676            array($source) // no cyclic link
1677            );
1678
1679          if (count($missing_destinations) > 0)
1680          {
1681            $destinations_of[$source] = array_unique(
1682              array_merge(
1683                $destinations,
1684                $missing_destinations
1685                )
1686              );
1687
1688            // a category has a least one new destination, we have to check
1689            // one more time that it doesn't generate more destinations
1690            $need_new_turn = true;
1691          }
1692        }
1693      }
1694    }
1695  } while ($need_new_turn);
1696
1697  if (is_array($categories))
1698  {
1699    $filtered_destinations_of = array();
1700
1701    // Even if there is no destinations for the requested categories, we
1702    // return empty arrays
1703    foreach ($categories as $category)
1704    {
1705      $filtered_destinations_of[$category] = array();
1706    }
1707
1708    foreach ($destinations_of as $source => $destinations)
1709    {
1710      if (in_array($source, $categories))
1711      {
1712        $filtered_destinations_of[$source] = $destinations;
1713      }
1714    }
1715
1716    return $filtered_destinations_of;
1717  }
1718  else
1719  {
1720    return $destinations_of;
1721  }
1722}
1723
1724/**
1725 * Returns all sources of a list of destination categories. This function
1726 * solves transitivity.
1727 *
1728 * @param mixed array of category ids, empty for all categories
1729 */
1730function get_sources($categories = 'all')
1731{
1732  $destinations_of = get_destinations();
1733
1734  $sources_of = array();
1735
1736  foreach ($destinations_of as $source => $destinations)
1737  {
1738    foreach ($destinations as $destination)
1739    {
1740      if (!isset($sources_of[$destination]))
1741      {
1742        $sources_of[$destination] = array();
1743      }
1744
1745      array_push($sources_of[$destination], $source);
1746    }
1747  }
1748
1749  // eventually, filter
1750  if (is_array($categories))
1751  {
1752    $filtered_sources_of = array();
1753
1754    // Even if there is no sources for the requested categories, we return
1755    // empty arrays
1756    foreach ($categories as $category)
1757    {
1758      $filtered_sources_of[$category] = array();
1759    }
1760
1761    foreach ($sources_of as $destination => $sources)
1762    {
1763      if (in_array($destination, $categories))
1764      {
1765        $filtered_sources_of[$destination] = $sources;
1766      }
1767    }
1768
1769    return $filtered_sources_of;
1770  }
1771  else
1772  {
1773    return $sources_of;
1774  }
1775}
1776
1777/**
1778 * Checks categories links are respected for a given list of destinations.
1779 *
1780 * Checking categories links means that each destination must be associated
1781 * to the images of its sources.
1782 *
1783 * @param mixed source category ids
1784 */
1785function check_links($destinations = 'all')
1786{
1787  $sources_of = get_sources($destinations);
1788
1789  if (empty($sources_of))
1790  {
1791    return true;
1792  }
1793
1794  // we need to search images of all sources and destinations
1795  $images_of = array();
1796
1797  foreach ($sources_of as $destination => $sources)
1798  {
1799    $images_of[$destination] = array();
1800
1801    foreach ($sources as $source)
1802    {
1803      $images_of[$source] = array();
1804    }
1805  }
1806
1807  $query = '
1808SELECT image_id, category_id
1809  FROM '.IMAGE_CATEGORY_TABLE.'
1810  WHERE category_id IN ('.
1811    implode(',', array_keys($images_of)).
1812    ')
1813;';
1814  $result = pwg_query($query);
1815
1816  while ($row = mysql_fetch_array($result))
1817  {
1818    array_push(
1819      $images_of[ $row['category_id'] ],
1820      $row['image_id']
1821      );
1822  }
1823
1824  $inserts = array();
1825
1826  foreach ($sources_of as $destination => $sources)
1827  {
1828    // merge all images from the sources of this destination
1829    $sources_images = array();
1830
1831    foreach ($sources as $source)
1832    {
1833      $sources_images = array_merge(
1834        $sources_images,
1835        $images_of[$source]
1836        );
1837    }
1838
1839    $sources_images = array_unique($sources_images);
1840
1841    // are there images among the sources that are not linked to the
1842    // destination?
1843    $missing_images = array_diff(
1844      $sources_images,
1845      $images_of[$destination]
1846      );
1847
1848    // if we find missing images (missing links in reality), we prepare the
1849    // final mass_inserts
1850    if (count($missing_images) > 0)
1851    {
1852      foreach ($missing_images as $missing_image)
1853      {
1854        array_push(
1855          $inserts,
1856          array(
1857            'category_id' => $destination,
1858            'image_id'    => $missing_image,
1859            )
1860          );
1861      }
1862    }
1863  }
1864
1865  if (count($inserts) > 0)
1866  {
1867    mass_inserts(
1868      IMAGE_CATEGORY_TABLE,
1869      array_keys($inserts[0]),
1870      $inserts
1871      );
1872  }
1873}
1874
1875/**
1876 * Based on categories links, delete image_category links on destinations.
1877 *
1878 * The rule is the following: if an image belong to the category and to the
1879 * source, we suppose it comes from the source. If the source/destination
1880 * link is broken, we delete the image/category link if the only origin of
1881 * the link was the broken categories link.
1882 *
1883 * Example: "=>" means "source of". Between brackets the associated images.
1884 *
1885 * A (1,2,9) => \
1886 *               |=> C (1,2,3,4,5,9) => D (1,2,3,4,5,6,9)
1887 * B (3,4,9) => /
1888 *
1889 * In category C, we suppose (1,2) come from A, (3,4) from B, 9 from A or B
1890 * and 5 was manually added. In category D, 6 was added manually.
1891 *
1892 * If we break A=>C, C and D loose (1,2) but not 9 because it can come from
1893 * B. If we break C=>D, D loose (3,4,5,9) but not 6 because it was
1894 * associated manually to 9.
1895 *
1896 * Warning: only virtual links can be removed, physical links are protected.
1897 *
1898 * @param int destination
1899 * @param array sources
1900 */
1901function delete_sources($destination, $sources)
1902{
1903  // if no sources to unlink, we stop with OK status
1904  if (count($sources) == 0)
1905  {
1906    return true;
1907  }
1908
1909  $query = '
1910DELETE
1911  FROM '.CATEGORIES_LINK_TABLE.'
1912  WHERE destination = '.$destination.'
1913    AND source IN ('.implode(',', $sources).')
1914;';
1915  pwg_query($query);
1916
1917  // The strategy is the following:
1918  //
1919  // * first we brutally delete the image/category associations on
1920  // destinations categories for all images belonging to sources.
1921  //
1922  // * then we check_links on destinations to rebuild missing image/category
1923  // associations.
1924
1925  // what are the images associated to the sources to unlink
1926  $query = '
1927SELECT image_id
1928  FROM '.IMAGE_CATEGORY_TABLE.'
1929  WHERE category_id IN ('.
1930    implode(',', $sources).
1931    ')
1932;';
1933  $sources_images = array_unique(
1934    array_from_query($query, 'image_id')
1935    );
1936
1937  if (count($sources_images) == 0)
1938  {
1939    return true;
1940  }
1941
1942  // retrieve all direct and indirect destinations of the current
1943  // destination
1944  $destinations_of = get_destinations(array($destination));
1945
1946  $destinations = array_merge(
1947    array($destination),
1948    $destinations_of[$destination]
1949    );
1950
1951  // unlink sources images from destinations
1952  $query = '
1953DELETE
1954  FROM '.IMAGE_CATEGORY_TABLE.'
1955  WHERE category_id IN ('.implode(',', $destinations).')
1956    AND image_id IN ('.implode(',', $sources_images).')
1957    AND is_storage = \'false\'
1958;';
1959  pwg_query($query);
1960
1961  // if the representative thumbnail of destinations was a picture from
1962  // $sources_images, we request a new random representant
1963  $query = '
1964SELECT id, representative_picture_id
1965  FROM '.CATEGORIES_TABLE.'
1966  WHERE id IN ('.implode(',', $destinations).')
1967;';
1968  $result = pwg_query($query);
1969
1970  $request_random = array();
1971
1972  while ($row = mysql_fetch_array($result))
1973  {
1974    if (isset($row['representative_picture_id']))
1975    {
1976      if (in_array($row['representative_picture_id'], $sources_images))
1977      {
1978        array_push($request_random, $row['id']);
1979      }
1980    }
1981  }
1982
1983  set_random_representant($request_random);
1984
1985  // eventually, we check_links to rebuild missing associations
1986  check_links($destinations);
1987
1988  return true;
1989}
1990
1991/**
1992 * create a virtual category
1993 *
1994 * @param string category name
1995 * @param int parent category id
1996 * @return array with ('info' and 'id') or ('error') key
1997 */
1998function create_virtual_category($category_name, $parent_id=null)
1999{
2000  global $conf;
2001
2002  // is the given category name only containing blank spaces ?
2003  if (preg_match('/^\s*$/', $category_name))
2004  {
2005    return array('error' => l10n('cat_error_name'));
2006  }
2007
2008  $parent_id = !empty($parent_id) ? $parent_id : 'NULL';
2009
2010  $query = '
2011SELECT MAX(rank)
2012  FROM '.CATEGORIES_TABLE.'
2013  WHERE id_uppercat '.(is_numeric($parent_id) ? '= '.$parent_id : 'IS NULL').'
2014;';
2015  list($current_rank) = mysql_fetch_array(pwg_query($query));
2016
2017  $insert = array(
2018    'name' => $category_name,
2019    'rank' => ++$current_rank,
2020    'commentable' => $conf['newcat_default_commentable'],
2021    'uploadable' => 'false',
2022    );
2023
2024  if ($parent_id != 'NULL')
2025  {
2026    $query = '
2027SELECT id, uppercats, global_rank, visible, status
2028  FROM '.CATEGORIES_TABLE.'
2029  WHERE id = '.$parent_id.'
2030;';
2031    $parent = mysql_fetch_array(pwg_query($query));
2032
2033    $insert{'id_uppercat'} = $parent{'id'};
2034    $insert{'global_rank'} = $parent{'global_rank'}.'.'.$insert{'rank'};
2035
2036    // at creation, must a category be visible or not ? Warning : if the
2037    // parent category is invisible, the category is automatically create
2038    // invisible. (invisible = locked)
2039    if ('false' == $parent['visible'])
2040    {
2041      $insert{'visible'} = 'false';
2042    }
2043    else
2044    {
2045      $insert{'visible'} = $conf['newcat_default_visible'];
2046    }
2047
2048    // at creation, must a category be public or private ? Warning : if the
2049    // parent category is private, the category is automatically create
2050    // private.
2051    if ('private' == $parent['status'])
2052    {
2053      $insert{'status'} = 'private';
2054    }
2055    else
2056    {
2057      $insert{'status'} = $conf['newcat_default_status'];
2058    }
2059  }
2060  else
2061  {
2062    $insert{'visible'} = $conf['newcat_default_visible'];
2063    $insert{'status'} = $conf['newcat_default_status'];
2064    $insert{'global_rank'} = $insert{'rank'};
2065  }
2066
2067  // we have then to add the virtual category
2068  mass_inserts(
2069    CATEGORIES_TABLE,
2070    array(
2071      'site_id', 'name', 'id_uppercat', 'rank', 'commentable',
2072      'uploadable', 'visible', 'status', 'global_rank',
2073      ),
2074    array($insert)
2075    );
2076
2077  $inserted_id = mysql_insert_id();
2078
2079  $query = '
2080UPDATE
2081  '.CATEGORIES_TABLE.'
2082  SET uppercats = \''.
2083    (isset($parent) ? $parent{'uppercats'}.',' : '').
2084    $inserted_id.
2085    '\'
2086  WHERE id = '.$inserted_id.'
2087;';
2088  pwg_query($query);
2089
2090  return array(
2091    'info' => l10n('cat_virtual_added'),
2092    'id'   => $inserted_id,
2093    );
2094}
2095
2096/**
2097 * Do maintenance on all PWG tables
2098 *
2099 * @return nono
2100 */
2101function do_maintenance_all_tables()
2102{
2103  global $prefixeTable;
2104 
2105  $all_tables = array();
2106
2107  // List all tables
2108  $query = 'SHOW TABLES LIKE \''.$prefixeTable.'%\';';
2109  $result = pwg_query($query);
2110  while ($row = mysql_fetch_array($result))
2111  {
2112    array_push($all_tables, $row[0]);
2113  }
2114
2115  // Repair all tables
2116  $query = 'REPAIR TABLE '.implode(', ', $all_tables).';';
2117  pwg_query($query);
2118
2119  // Re-Order all tables
2120  foreach ($all_tables as $table_name)
2121  {
2122    $all_primary_key = array();
2123   
2124    $query = 'DESC '.$table_name.';';
2125    $result = pwg_query($query);
2126    while ($row = mysql_fetch_array($result))
2127    {
2128      if ($row['Key'] == 'PRI')
2129      {
2130        array_push($all_primary_key, $row['Field']);
2131      }
2132    }
2133   
2134    if (count($all_primary_key) != 0)
2135    {
2136      $query = 'ALTER TABLE '.$table_name.' ORDER BY '.implode(', ', $all_primary_key).';';
2137      pwg_query($query);
2138    }
2139  }
2140
2141  // Optimize all tables
2142  $query = 'OPTIMIZE TABLE '.implode(', ', $all_tables).';';
2143  pwg_query($query);
2144
2145}
2146
2147
2148?>
Note: See TracBrowser for help on using the repository browser.