source: trunk/include/functions.inc.php @ 18766

Last change on this file since 18766 was 18729, checked in by rvelices, 12 years ago
  • navigation bar urls are always on the number of images per page boundaries (fix rare case when the admin changer the number of thumbs per page for guests, and then google bot fetches almost all start-xx pages)
  • Property svn:eol-style set to LF
File size: 42.9 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2012 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24include_once( PHPWG_ROOT_PATH .'include/functions_user.inc.php' );
25include_once( PHPWG_ROOT_PATH .'include/functions_cookie.inc.php' );
26include_once( PHPWG_ROOT_PATH .'include/functions_session.inc.php' );
27include_once( PHPWG_ROOT_PATH .'include/functions_category.inc.php' );
28include_once( PHPWG_ROOT_PATH .'include/functions_html.inc.php' );
29include_once( PHPWG_ROOT_PATH .'include/functions_tag.inc.php' );
30include_once( PHPWG_ROOT_PATH .'include/functions_url.inc.php' );
31include_once( PHPWG_ROOT_PATH .'include/functions_plugins.inc.php' );
32include_once( PHPWG_ROOT_PATH .'include/derivative_params.inc.php');
33include_once( PHPWG_ROOT_PATH .'include/derivative_std_params.inc.php');
34include_once( PHPWG_ROOT_PATH .'include/derivative.inc.php');
35require_once( PHPWG_ROOT_PATH .'include/smarty/libs/Smarty.class.php');
36include_once( PHPWG_ROOT_PATH .'include/template.class.php');
37
38//----------------------------------------------------------- generic functions
39
40/**
41 * stupidly returns the current microsecond since Unix epoch
42 */
43function micro_seconds()
44{
45  $t1 = explode(' ', microtime());
46  $t2 = explode('.', $t1[0]);
47  $t2 = $t1[1].substr($t2[1], 0, 6);
48  return $t2;
49}
50
51// The function get_moment returns a float value coresponding to the number
52// of seconds since the unix epoch (1st January 1970) and the microseconds
53// are precised : e.g. 1052343429.89276600
54function get_moment()
55{
56  return microtime(true);
57}
58
59// The function get_elapsed_time returns the number of seconds (with 3
60// decimals precision) between the start time and the end time given.
61function get_elapsed_time( $start, $end )
62{
63  return number_format( $end - $start, 3, '.', ' ').' s';
64}
65
66// - The replace_space function replaces space and '-' characters
67//   by their HTML equivalent  &nbsb; and &minus;
68// - The function does not replace characters in HTML tags
69// - This function was created because IE5 does not respect the
70//   CSS "white-space: nowrap;" property unless space and minus
71//   characters are replaced like this function does.
72// - Example :
73//                 <div class="foo">My friend</div>
74//               ( 01234567891111111111222222222233 )
75//               (           0123456789012345678901 )
76// becomes :
77//             <div class="foo">My&nbsp;friend</div>
78function replace_space( $string )
79{
80  //return $string;
81  $return_string = '';
82  // $remaining is the rest of the string where to replace spaces characters
83  $remaining = $string;
84  // $start represents the position of the next '<' character
85  // $end   represents the position of the next '>' character
86  ; // -> 0
87  $end   = strpos ( $remaining, '>' ); // -> 16
88  // as long as a '<' and his friend '>' are found, we loop
89  while ( ($start=strpos( $remaining, '<' )) !==false
90        and ($end=strpos( $remaining, '>' )) !== false )
91  {
92    // $treatment is the part of the string to treat
93    // In the first loop of our example, this variable is empty, but in the
94    // second loop, it equals 'My friend'
95    $treatment = substr ( $remaining, 0, $start );
96    // Replacement of ' ' by his equivalent '&nbsp;'
97    $treatment = str_replace( ' ', '&nbsp;', $treatment );
98    $treatment = str_replace( '-', '&minus;', $treatment );
99    // composing the string to return by adding the treated string and the
100    // following HTML tag -> 'My&nbsp;friend</div>'
101    $return_string.= $treatment.substr( $remaining, $start, $end-$start+1 );
102    // the remaining string is deplaced to the part after the '>' of this
103    // loop
104    $remaining = substr ( $remaining, $end + 1, strlen( $remaining ) );
105  }
106  $treatment = str_replace( ' ', '&nbsp;', $remaining );
107  $treatment = str_replace( '-', '&minus;', $treatment );
108  $return_string.= $treatment;
109
110  return $return_string;
111}
112
113// get_extension returns the part of the string after the last "."
114function get_extension( $filename )
115{
116  return substr( strrchr( $filename, '.' ), 1, strlen ( $filename ) );
117}
118
119// get_filename_wo_extension returns the part of the string before the last
120// ".".
121// get_filename_wo_extension( 'test.tar.gz' ) -> 'test.tar'
122function get_filename_wo_extension( $filename )
123{
124  $pos = strrpos( $filename, '.' );
125  return ($pos===false) ? $filename : substr( $filename, 0, $pos);
126}
127
128define('MKGETDIR_NONE', 0);
129define('MKGETDIR_RECURSIVE', 1);
130define('MKGETDIR_DIE_ON_ERROR', 2);
131define('MKGETDIR_PROTECT_INDEX', 4);
132define('MKGETDIR_PROTECT_HTACCESS', 8);
133define('MKGETDIR_DEFAULT', 7);
134/**
135 * creates directory if not exists; ensures that directory is writable
136 * @param:
137 *  string $dir
138 *  int $flags combination of MKGETDIR_xxx
139 * @return bool false on error else true
140 */
141function mkgetdir($dir, $flags=MKGETDIR_DEFAULT)
142{
143  if ( !is_dir($dir) )
144  {
145    global $conf;
146    if (substr(PHP_OS, 0, 3) == 'WIN')
147    {
148      $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir);
149    }
150    $umask = umask(0);
151    $mkd = @mkdir($dir, $conf['chmod_value'], ($flags&MKGETDIR_RECURSIVE) ? true:false );
152    umask($umask);
153    if ($mkd==false)
154    {
155      !($flags&MKGETDIR_DIE_ON_ERROR) or fatal_error( "$dir ".l10n('no write access'));
156      return false;
157    }
158    if( $flags&MKGETDIR_PROTECT_HTACCESS )
159    {
160      $file = $dir.'/.htaccess';
161      file_exists($file) or @file_put_contents( $file, 'deny from all' );
162    }
163    if( $flags&MKGETDIR_PROTECT_INDEX )
164    {
165      $file = $dir.'/index.htm';
166      file_exists($file) or @file_put_contents( $file, 'Not allowed!' );
167    }
168  }
169  if ( !is_writable($dir) )
170  {
171    !($flags&MKGETDIR_DIE_ON_ERROR) or fatal_error( "$dir ".l10n('no write access'));
172    return false;
173  }
174  return true;
175}
176
177/* Returns true if the string appears to be encoded in UTF-8. (from wordpress)
178 * @param string Str
179 */
180function seems_utf8($Str) {
181  // OBSOLETE !!!
182  return qualify_utf8($Str) >= 0;
183}
184
185/* returns 0 if $str is Ascii, 1 if utf-8, -1 otherwise */
186function qualify_utf8($Str)
187{
188  $ret = 0;
189  for ($i=0; $i<strlen($Str); $i++) {
190    if (ord($Str[$i]) < 0x80) continue; # 0bbbbbbb
191    $ret = 1;
192    if ((ord($Str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
193    elseif ((ord($Str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
194    elseif ((ord($Str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
195    elseif ((ord($Str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
196    elseif ((ord($Str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
197    else return -1; # Does not match any model
198    for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
199      if ((++$i == strlen($Str)) || ((ord($Str[$i]) & 0xC0) != 0x80))
200        return -1;
201    }
202  }
203  return $ret;
204}
205
206/* Remove accents from a UTF-8 or ISO-859-1 string (from wordpress)
207 * @param string sstring - an UTF-8 or ISO-8859-1 string
208 */
209function remove_accents($string)
210{
211  $utf = qualify_utf8($string);
212  if ( $utf == 0 )
213    return $string; // ascii
214
215  if ( $utf > 0 ) {
216    $chars = array(
217    // Decompositions for Latin-1 Supplement
218    "\xc3\x80"=>'A', "\xc3\x81"=>'A',
219    "\xc3\x82"=>'A', "\xc3\x83"=>'A',
220    "\xc3\x84"=>'A', "\xc3\x85"=>'A',
221    "\xc3\x87"=>'C', "\xc3\x88"=>'E',
222    "\xc3\x89"=>'E', "\xc3\x8a"=>'E',
223    "\xc3\x8b"=>'E', "\xc3\x8c"=>'I',
224    "\xc3\x8d"=>'I', "\xc3\x8e"=>'I',
225    "\xc3\x8f"=>'I', "\xc3\x91"=>'N',
226    "\xc3\x92"=>'O', "\xc3\x93"=>'O',
227    "\xc3\x94"=>'O', "\xc3\x95"=>'O',
228    "\xc3\x96"=>'O', "\xc3\x99"=>'U',
229    "\xc3\x9a"=>'U', "\xc3\x9b"=>'U',
230    "\xc3\x9c"=>'U', "\xc3\x9d"=>'Y',
231    "\xc3\x9f"=>'s', "\xc3\xa0"=>'a',
232    "\xc3\xa1"=>'a', "\xc3\xa2"=>'a',
233    "\xc3\xa3"=>'a', "\xc3\xa4"=>'a',
234    "\xc3\xa5"=>'a', "\xc3\xa7"=>'c',
235    "\xc3\xa8"=>'e', "\xc3\xa9"=>'e',
236    "\xc3\xaa"=>'e', "\xc3\xab"=>'e',
237    "\xc3\xac"=>'i', "\xc3\xad"=>'i',
238    "\xc3\xae"=>'i', "\xc3\xaf"=>'i',
239    "\xc3\xb1"=>'n', "\xc3\xb2"=>'o',
240    "\xc3\xb3"=>'o', "\xc3\xb4"=>'o',
241    "\xc3\xb5"=>'o', "\xc3\xb6"=>'o',
242    "\xc3\xb9"=>'u', "\xc3\xba"=>'u',
243    "\xc3\xbb"=>'u', "\xc3\xbc"=>'u',
244    "\xc3\xbd"=>'y', "\xc3\xbf"=>'y',
245    // Decompositions for Latin Extended-A
246    "\xc4\x80"=>'A', "\xc4\x81"=>'a',
247    "\xc4\x82"=>'A', "\xc4\x83"=>'a',
248    "\xc4\x84"=>'A', "\xc4\x85"=>'a',
249    "\xc4\x86"=>'C', "\xc4\x87"=>'c',
250    "\xc4\x88"=>'C', "\xc4\x89"=>'c',
251    "\xc4\x8a"=>'C', "\xc4\x8b"=>'c',
252    "\xc4\x8c"=>'C', "\xc4\x8d"=>'c',
253    "\xc4\x8e"=>'D', "\xc4\x8f"=>'d',
254    "\xc4\x90"=>'D', "\xc4\x91"=>'d',
255    "\xc4\x92"=>'E', "\xc4\x93"=>'e',
256    "\xc4\x94"=>'E', "\xc4\x95"=>'e',
257    "\xc4\x96"=>'E', "\xc4\x97"=>'e',
258    "\xc4\x98"=>'E', "\xc4\x99"=>'e',
259    "\xc4\x9a"=>'E', "\xc4\x9b"=>'e',
260    "\xc4\x9c"=>'G', "\xc4\x9d"=>'g',
261    "\xc4\x9e"=>'G', "\xc4\x9f"=>'g',
262    "\xc4\xa0"=>'G', "\xc4\xa1"=>'g',
263    "\xc4\xa2"=>'G', "\xc4\xa3"=>'g',
264    "\xc4\xa4"=>'H', "\xc4\xa5"=>'h',
265    "\xc4\xa6"=>'H', "\xc4\xa7"=>'h',
266    "\xc4\xa8"=>'I', "\xc4\xa9"=>'i',
267    "\xc4\xaa"=>'I', "\xc4\xab"=>'i',
268    "\xc4\xac"=>'I', "\xc4\xad"=>'i',
269    "\xc4\xae"=>'I', "\xc4\xaf"=>'i',
270    "\xc4\xb0"=>'I', "\xc4\xb1"=>'i',
271    "\xc4\xb2"=>'IJ', "\xc4\xb3"=>'ij',
272    "\xc4\xb4"=>'J', "\xc4\xb5"=>'j',
273    "\xc4\xb6"=>'K', "\xc4\xb7"=>'k',
274    "\xc4\xb8"=>'k', "\xc4\xb9"=>'L',
275    "\xc4\xba"=>'l', "\xc4\xbb"=>'L',
276    "\xc4\xbc"=>'l', "\xc4\xbd"=>'L',
277    "\xc4\xbe"=>'l', "\xc4\xbf"=>'L',
278    "\xc5\x80"=>'l', "\xc5\x81"=>'L',
279    "\xc5\x82"=>'l', "\xc5\x83"=>'N',
280    "\xc5\x84"=>'n', "\xc5\x85"=>'N',
281    "\xc5\x86"=>'n', "\xc5\x87"=>'N',
282    "\xc5\x88"=>'n', "\xc5\x89"=>'N',
283    "\xc5\x8a"=>'n', "\xc5\x8b"=>'N',
284    "\xc5\x8c"=>'O', "\xc5\x8d"=>'o',
285    "\xc5\x8e"=>'O', "\xc5\x8f"=>'o',
286    "\xc5\x90"=>'O', "\xc5\x91"=>'o',
287    "\xc5\x92"=>'OE', "\xc5\x93"=>'oe',
288    "\xc5\x94"=>'R', "\xc5\x95"=>'r',
289    "\xc5\x96"=>'R', "\xc5\x97"=>'r',
290    "\xc5\x98"=>'R', "\xc5\x99"=>'r',
291    "\xc5\x9a"=>'S', "\xc5\x9b"=>'s',
292    "\xc5\x9c"=>'S', "\xc5\x9d"=>'s',
293    "\xc5\x9e"=>'S', "\xc5\x9f"=>'s',
294    "\xc5\xa0"=>'S', "\xc5\xa1"=>'s',
295    "\xc5\xa2"=>'T', "\xc5\xa3"=>'t',
296    "\xc5\xa4"=>'T', "\xc5\xa5"=>'t',
297    "\xc5\xa6"=>'T', "\xc5\xa7"=>'t',
298    "\xc5\xa8"=>'U', "\xc5\xa9"=>'u',
299    "\xc5\xaa"=>'U', "\xc5\xab"=>'u',
300    "\xc5\xac"=>'U', "\xc5\xad"=>'u',
301    "\xc5\xae"=>'U', "\xc5\xaf"=>'u',
302    "\xc5\xb0"=>'U', "\xc5\xb1"=>'u',
303    "\xc5\xb2"=>'U', "\xc5\xb3"=>'u',
304    "\xc5\xb4"=>'W', "\xc5\xb5"=>'w',
305    "\xc5\xb6"=>'Y', "\xc5\xb7"=>'y',
306    "\xc5\xb8"=>'Y', "\xc5\xb9"=>'Z',
307    "\xc5\xba"=>'z', "\xc5\xbb"=>'Z',
308    "\xc5\xbc"=>'z', "\xc5\xbd"=>'Z',
309    "\xc5\xbe"=>'z', "\xc5\xbf"=>'s',
310    // Decompositions for Latin Extended-B
311    "\xc8\x98"=>'S', "\xc8\x99"=>'s',
312    "\xc8\x9a"=>'T', "\xc8\x9b"=>'t',
313    // Euro Sign
314    "\xe2\x82\xac"=>'E',
315    // GBP (Pound) Sign
316    "\xc2\xa3"=>'');
317
318    $string = strtr($string, $chars);
319  } else {
320    // Assume ISO-8859-1 if not UTF-8
321    $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
322      .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
323      .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
324      .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
325      .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
326      .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
327      .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
328      .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
329      .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
330      .chr(252).chr(253).chr(255);
331
332    $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
333
334    $string = strtr($string, $chars['in'], $chars['out']);
335    $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
336    $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
337    $string = str_replace($double_chars['in'], $double_chars['out'], $string);
338  }
339
340  return $string;
341}
342
343if (function_exists('mb_strtolower') && defined('PWG_CHARSET'))
344{
345  function transliterate($term)
346  {
347    return remove_accents( mb_strtolower($term, PWG_CHARSET) );
348  }
349}
350else
351{
352  function transliterate($term)
353  {
354    return remove_accents( strtolower($term) );
355  }
356}
357
358
359
360/**
361 * simplify a string to insert it into an URL
362 *
363 * @param string
364 * @return string
365 */
366function str2url($str)
367{
368  $str = $safe = transliterate($str);
369  $str = preg_replace('/[^\x80-\xffa-z0-9_\s\'\:\/\[\],-]/','',$str);
370  $str = preg_replace('/[\s\'\:\/\[\],-]+/',' ',trim($str));
371  $res = str_replace(' ','_',$str);
372
373  if (empty($res))
374  {
375    $res = str_replace(' ','_', $safe);
376  }
377
378  return $res;
379}
380
381//-------------------------------------------- Piwigo specific functions
382
383/**
384 * returns an array with a list of {language_code => language_name}
385 *
386 * @returns array
387 */
388function get_languages()
389{
390  $query = '
391SELECT id, name
392  FROM '.LANGUAGES_TABLE.'
393  ORDER BY name ASC
394;';
395  $result = pwg_query($query);
396
397  $languages = array();
398  while ($row = pwg_db_fetch_assoc($result))
399  {
400    if (is_dir(PHPWG_ROOT_PATH.'language/'.$row['id']))
401    {
402      $languages[ $row['id'] ] = $row['name'];
403    }
404  }
405
406  return $languages;
407}
408
409function pwg_log($image_id = null, $image_type = null)
410{
411  global $conf, $user, $page;
412
413  $do_log = $conf['log'];
414  if (is_admin())
415  {
416    $do_log = $conf['history_admin'];
417  }
418  if (is_a_guest())
419  {
420    $do_log = $conf['history_guest'];
421  }
422
423  $do_log = trigger_event('pwg_log_allowed', $do_log, $image_id, $image_type);
424
425  if (!$do_log)
426  {
427    return false;
428  }
429
430  $tags_string = null;
431  if ('tags'==@$page['section'])
432  {
433    $tags_string = implode(',', $page['tag_ids']);
434  }
435
436  $query = '
437INSERT INTO '.HISTORY_TABLE.'
438  (
439    date,
440    time,
441    user_id,
442    IP,
443    section,
444    category_id,
445    image_id,
446    image_type,
447    tag_ids
448  )
449  VALUES
450  (
451    CURRENT_DATE,
452    CURRENT_TIME,
453    '.$user['id'].',
454    \''.$_SERVER['REMOTE_ADDR'].'\',
455    '.(isset($page['section']) ? "'".$page['section']."'" : 'NULL').',
456    '.(isset($page['category']['id']) ? $page['category']['id'] : 'NULL').',
457    '.(isset($image_id) ? $image_id : 'NULL').',
458    '.(isset($image_type) ? "'".$image_type."'" : 'NULL').',
459    '.(isset($tags_string) ? "'".$tags_string."'" : 'NULL').'
460  )
461;';
462  pwg_query($query);
463
464  return true;
465}
466
467// format_date returns a formatted date for display. The date given in
468// argument must be an american format (2003-09-15). By option, you can show the time.
469// The output is internationalized.
470//
471// format_date( "2003-09-15", true ) -> "Monday 15 September 2003 21:52"
472function format_date($date, $show_time = false, $show_day_name = true)
473{
474  global $lang;
475
476  if (strpos($date, '0') == 0)
477  {
478    return l10n('N/A');
479  }
480
481  $ymdhms = array();
482  $tok = strtok( $date, '- :');
483  while ($tok !== false)
484  {
485    $ymdhms[] = $tok;
486    $tok = strtok('- :');
487  }
488
489  if ( count($ymdhms)<3 )
490  {
491    return false;
492  }
493
494  $formated_date = '';
495  // before 1970, Microsoft Windows can't mktime
496  if ($ymdhms[0] >= 1970 and $ymdhms[1] != 0 and $ymdhms[2] != 0)
497  {
498    // we ask midday because Windows think it's prior to midnight with a
499    // zero and refuse to work
500    $formated_date.= $lang['day'][date('w', mktime(12,0,0,$ymdhms[1],$ymdhms[2],$ymdhms[0]))];
501  }
502
503  if ($ymdhms[2] != 0)
504  {
505    $formated_date.= ' '.$ymdhms[2];
506  }
507
508  if ($ymdhms[1] != 0)
509  {
510    $formated_date.= ' '.$lang['month'][(int)$ymdhms[1]];
511  }
512
513  $formated_date.= ' '.$ymdhms[0];
514  if ($show_time and count($ymdhms)>=5 )
515  {
516    $formated_date.= ' '.$ymdhms[3].':'.$ymdhms[4];
517  }
518  return $formated_date;
519}
520
521/**
522 * Works out the time since the entry post, takes a an argument in unix time or datetime
523 */
524function time_since($original, $stop = 'minute')
525{
526  if (!is_int($original))
527  {
528    $ymdhms = array();
529    $tok = strtok($original, '- :');
530    while ($tok !== false)
531    {
532      $ymdhms[] = $tok;
533      $tok = strtok('- :');
534    }
535
536    if ($ymdhms[0] < 1970) return false;
537    if (!isset($ymdhms[3])) $ymdhms[3] = 12;
538    if (!isset($ymdhms[4])) $ymdhms[4] = 0;
539    if (!isset($ymdhms[5])) $ymdhms[5] = 0;
540    $original = mktime($ymdhms[3],$ymdhms[4],$ymdhms[5],$ymdhms[1],$ymdhms[2],$ymdhms[0]);
541  }
542
543  // array of time period chunks
544  $chunks = array(
545    'year' => 60 * 60 * 24 * 365,
546    'month' => 60 * 60 * 24 * 30,
547    'week' => 60 * 60 * 24 * 7,
548    'day' => 60 * 60 * 24,
549    'hour' => 60 * 60,
550    'minute' => 60,
551    'second' => 1,
552  );
553
554  $today = time(); /* Current unix time  */
555  $since = abs($today - $original);
556
557  $print = null;
558  foreach ($chunks as $name => $seconds)
559  {
560    if (($count = floor($since / $seconds)) != 0)
561    {
562      $print.= l10n_dec('%d '.$name, '%d '.$name.'s', $count);
563      $since-= $count*$seconds;
564    }
565    if (!empty($print) and $chunks[$name] <= $chunks[$stop])
566    {
567      break;
568    }
569  }
570
571  if ($today > $original)
572  {
573    $print = sprintf(l10n('%s ago'), $print);
574  }
575  else
576  {
577    $print = sprintf(l10n('%s in the future'), $print);
578  }
579
580  return $print;
581}
582
583function pwg_debug( $string )
584{
585  global $debug,$t2,$page;
586
587  $now = explode( ' ', microtime() );
588  $now2 = explode( '.', $now[0] );
589  $now2 = $now[1].'.'.$now2[1];
590  $time = number_format( $now2 - $t2, 3, '.', ' ').' s';
591  $debug .= '<p>';
592  $debug.= '['.$time.', ';
593  $debug.= $page['count_queries'].' queries] : '.$string;
594  $debug.= "</p>\n";
595}
596
597/**
598 * Redirects to the given URL (HTTP method)
599 *
600 * Note : once this function called, the execution doesn't go further
601 * (presence of an exit() instruction.
602 *
603 * @param string $url
604 * @return void
605 */
606function redirect_http( $url )
607{
608  if (ob_get_length () !== FALSE)
609  {
610    ob_clean();
611  }
612  // default url is on html format
613  $url = html_entity_decode($url);
614  header('Request-URI: '.$url);
615  header('Content-Location: '.$url);
616  header('Location: '.$url);
617  exit();
618}
619
620/**
621 * Redirects to the given URL (HTML method)
622 *
623 * Note : once this function called, the execution doesn't go further
624 * (presence of an exit() instruction.
625 *
626 * @param string $url
627 * @param string $title_msg
628 * @param integer $refreh_time
629 * @return void
630 */
631function redirect_html( $url , $msg = '', $refresh_time = 0)
632{
633  global $user, $template, $lang_info, $conf, $lang, $t2, $page, $debug;
634
635  if (!isset($lang_info) || !isset($template) )
636  {
637    $user = build_user( $conf['guest_id'], true);
638    load_language('common.lang');
639    trigger_action('loading_lang');
640    load_language('lang', PHPWG_ROOT_PATH.PWG_LOCAL_DIR, array('no_fallback'=>true, 'local'=>true) );
641    $template = new Template(PHPWG_ROOT_PATH.'themes', get_default_theme());
642  }
643        elseif (defined('IN_ADMIN') and IN_ADMIN)
644        {
645                $template = new Template(PHPWG_ROOT_PATH.'themes', get_default_theme());
646        }
647
648  if (empty($msg))
649  {
650    $msg = nl2br(l10n('Redirection...'));
651  }
652
653  $refresh = $refresh_time;
654  $url_link = $url;
655  $title = 'redirection';
656
657  $template->set_filenames( array( 'redirect' => 'redirect.tpl' ) );
658
659  include( PHPWG_ROOT_PATH.'include/page_header.php' );
660
661  $template->set_filenames( array( 'redirect' => 'redirect.tpl' ) );
662  $template->assign('REDIRECT_MSG', $msg);
663
664  $template->parse('redirect');
665
666  include( PHPWG_ROOT_PATH.'include/page_tail.php' );
667
668  exit();
669}
670
671/**
672 * Redirects to the given URL (Switch to HTTP method or HTML method)
673 *
674 * Note : once this function called, the execution doesn't go further
675 * (presence of an exit() instruction.
676 *
677 * @param string $url
678 * @param string $title_msg
679 * @param integer $refreh_time
680 * @return void
681 */
682function redirect( $url , $msg = '', $refresh_time = 0)
683{
684  global $conf;
685
686  // with RefeshTime <> 0, only html must be used
687  if ($conf['default_redirect_method']=='http'
688      and $refresh_time==0
689      and !headers_sent()
690    )
691  {
692    redirect_http($url);
693  }
694  else
695  {
696    redirect_html($url, $msg, $refresh_time);
697  }
698}
699
700/**
701 * returns $_SERVER['QUERY_STRING'] whitout keys given in parameters
702 *
703 * @param array $rejects
704 * @param boolean $escape - if true escape & to &amp; (for html)
705 * @returns string
706 */
707function get_query_string_diff($rejects=array(), $escape=true)
708{
709  if (empty($_SERVER['QUERY_STRING']))
710  {
711    return '';
712  }
713
714  $query_string = '';
715
716  $str = $_SERVER['QUERY_STRING'];
717  parse_str($str, $vars);
718
719  $is_first = true;
720  foreach ($vars as $key => $value)
721  {
722    if (!in_array($key, $rejects))
723    {
724      $query_string.= $is_first ? '?' : ($escape ? '&amp;' : '&' );
725      $is_first = false;
726      $query_string.= $key.'='.$value;
727    }
728  }
729
730  return $query_string;
731}
732
733function url_is_remote($url)
734{
735  if ( strncmp($url, 'http://', 7)==0
736    or strncmp($url, 'https://', 8)==0 )
737  {
738    return true;
739  }
740  return false;
741}
742
743/**
744 * returns available themes
745 */
746function get_pwg_themes($show_mobile=false)
747{
748  global $conf;
749
750  $themes = array();
751
752  $query = '
753SELECT
754    id,
755    name
756  FROM '.THEMES_TABLE.'
757  ORDER BY name ASC
758;';
759  $result = pwg_query($query);
760  while ($row = pwg_db_fetch_assoc($result))
761  {
762    if ($row['id'] == $conf['mobile_theme'])
763    {
764      if (!$show_mobile)
765      {
766        continue;
767      }
768      $row['name'] .= ' ('.l10n('Mobile').')';
769    }
770    if (check_theme_installed($row['id']))
771    {
772      $themes[ $row['id'] ] = $row['name'];
773    }
774  }
775
776  // plugins want remove some themes based on user status maybe?
777  $themes = trigger_event('get_pwg_themes', $themes);
778
779  return $themes;
780}
781
782function check_theme_installed($theme_id)
783{
784  global $conf;
785
786  return file_exists($conf['themes_dir'].'/'.$theme_id.'/'.'themeconf.inc.php');
787}
788
789/** Transforms an original path to its pwg representative */
790function original_to_representative($path, $representative_ext)
791{
792  $pos = strrpos($path, '/');
793  $path = substr_replace($path, 'pwg_representative/', $pos+1, 0);
794  $pos = strrpos($path, '.');
795  return substr_replace($path, $representative_ext, $pos+1);
796}
797
798/**
799 * @param element_info array containing element information from db;
800 * at least 'id', 'path' should be present
801 */
802function get_element_path($element_info)
803{
804  $path = $element_info['path'];
805  if ( !url_is_remote($path) )
806  {
807    $path = PHPWG_ROOT_PATH.$path;
808  }
809  return $path;
810}
811
812
813/**
814 * fill the current user caddie with given elements, if not already in
815 * caddie
816 *
817 * @param array elements_id
818 */
819function fill_caddie($elements_id)
820{
821  global $user;
822
823  $query = '
824SELECT element_id
825  FROM '.CADDIE_TABLE.'
826  WHERE user_id = '.$user['id'].'
827;';
828  $in_caddie = array_from_query($query, 'element_id');
829
830  $caddiables = array_diff($elements_id, $in_caddie);
831
832  $datas = array();
833
834  foreach ($caddiables as $caddiable)
835  {
836    array_push($datas, array('element_id' => $caddiable,
837                             'user_id' => $user['id']));
838  }
839
840  if (count($caddiables) > 0)
841  {
842    mass_inserts(CADDIE_TABLE, array('element_id','user_id'), $datas);
843  }
844}
845
846/**
847 * returns the element name from its filename
848 *
849 * @param string filename
850 * @return string name
851 */
852function get_name_from_file($filename)
853{
854  return str_replace('_',' ',get_filename_wo_extension($filename));
855}
856
857/**
858 * returns the corresponding value from $lang if existing. Else, the key is
859 * returned
860 *
861 * @param string key
862 * @return string
863 */
864function l10n($key)
865{
866  global $lang, $conf;
867
868  if ( ($val=@$lang[$key]) == null)
869  {
870    if ($conf['debug_l10n'] and !isset($lang[$key]) and !empty($key))
871    {
872      trigger_error('[l10n] language key "'.$key.'" is not defined', E_USER_WARNING);
873    }
874    $val = $key;
875  }
876  return $val;
877}
878
879/**
880 * returns the prinft value for strings including %d
881 * return is concorded with decimal value (singular, plural)
882 *
883 * @param singular string key
884 * @param plural string key
885 * @param decimal value
886 * @return string
887 */
888function l10n_dec($singular_fmt_key, $plural_fmt_key, $decimal)
889{
890  global $lang_info;
891
892  return
893    sprintf(
894      l10n((
895        (($decimal > 1) or ($decimal == 0 and $lang_info['zero_plural']))
896          ? $plural_fmt_key
897          : $singular_fmt_key
898        )), $decimal);
899}
900
901/*
902 * returns a single element to use with l10n_args
903 *
904 * @param string key: translation key
905 * @param array/string/../number args:
906 *   arguments to use on sprintf($key, args)
907 *   if args is a array, each values are used on sprintf
908 * @return string
909 */
910function get_l10n_args($key, $args)
911{
912  if (is_array($args))
913  {
914    $key_arg = array_merge(array($key), $args);
915  }
916  else
917  {
918    $key_arg = array($key,  $args);
919  }
920  return array('key_args' => $key_arg);
921}
922
923/*
924 * returns a string with formated with l10n_args elements
925 *
926 * @param element/array $key_args: element or array of l10n_args elements
927 * @param $sep: if $key_args is array,
928 *   separator is used when translated l10n_args elements are concated
929 * @return string
930 */
931function l10n_args($key_args, $sep = "\n")
932{
933  if (is_array($key_args))
934  {
935    foreach ($key_args as $key => $element)
936    {
937      if (isset($result))
938      {
939        $result .= $sep;
940      }
941      else
942      {
943        $result = '';
944      }
945
946      if ($key === 'key_args')
947      {
948        array_unshift($element, l10n(array_shift($element)));
949        $result .= call_user_func_array('sprintf', $element);
950      }
951      else
952      {
953        $result .= l10n_args($element, $sep);
954      }
955    }
956  }
957  else
958  {
959    fatal_error('l10n_args: Invalid arguments');
960  }
961
962  return $result;
963}
964
965/**
966 * returns the corresponding value from $themeconf if existing. Else, the
967 * key is returned
968 *
969 * @param string key
970 * @return string
971 */
972function get_themeconf($key)
973{
974  global $template;
975
976  return $template->get_themeconf($key);
977}
978
979/**
980 * Returns webmaster mail address depending on $conf['webmaster_id']
981 *
982 * @return string
983 */
984function get_webmaster_mail_address()
985{
986  global $conf;
987
988  $query = '
989SELECT '.$conf['user_fields']['email'].'
990  FROM '.USERS_TABLE.'
991  WHERE '.$conf['user_fields']['id'].' = '.$conf['webmaster_id'].'
992;';
993  list($email) = pwg_db_fetch_row(pwg_query($query));
994
995  return $email;
996}
997
998/**
999 * Add configuration parameters from database to global $conf array
1000 *
1001 * @return void
1002 */
1003function load_conf_from_db($condition = '')
1004{
1005  global $conf;
1006
1007  $query = '
1008SELECT param, value
1009 FROM '.CONFIG_TABLE.'
1010 '.(!empty($condition) ? 'WHERE '.$condition : '').'
1011;';
1012  $result = pwg_query($query);
1013
1014  if ((pwg_db_num_rows($result) == 0) and !empty($condition))
1015  {
1016    fatal_error('No configuration data');
1017  }
1018
1019  while ($row = pwg_db_fetch_assoc($result))
1020  {
1021    $val = isset($row['value']) ? $row['value'] : '';
1022    // If the field is true or false, the variable is transformed into a boolean value.
1023    if ($val == 'true')
1024    {
1025      $val = true;
1026    }
1027    elseif ($val == 'false')
1028    {
1029      $val = false;
1030    }
1031    $conf[ $row['param'] ] = $val;
1032  }
1033}
1034
1035function conf_update_param($param, $value)
1036{
1037  $query = '
1038SELECT
1039    param,
1040    value
1041  FROM '.CONFIG_TABLE.'
1042  WHERE param = \''.$param.'\'
1043;';
1044  $params = array_from_query($query, 'param');
1045
1046  if (count($params) == 0)
1047  {
1048    $query = '
1049INSERT
1050  INTO '.CONFIG_TABLE.'
1051  (param, value)
1052  VALUES(\''.$param.'\', \''.$value.'\')
1053;';
1054    pwg_query($query);
1055  }
1056  else
1057  {
1058    $query = '
1059UPDATE '.CONFIG_TABLE.'
1060  SET value = \''.$value.'\'
1061  WHERE param = \''.$param.'\'
1062;';
1063    pwg_query($query);
1064  }
1065}
1066
1067/**
1068 * Prepends and appends a string at each value of the given array.
1069 *
1070 * @param array
1071 * @param string prefix to each array values
1072 * @param string suffix to each array values
1073 */
1074function prepend_append_array_items($array, $prepend_str, $append_str)
1075{
1076  array_walk(
1077    $array,
1078    create_function('&$s', '$s = "'.$prepend_str.'".$s."'.$append_str.'";')
1079    );
1080
1081  return $array;
1082}
1083
1084/**
1085 * creates an hashed based on a query, this function is a very common
1086 * pattern used here. Among the selected columns fetched, choose one to be
1087 * the key, another one to be the value.
1088 *
1089 * @param string $query
1090 * @param string $keyname
1091 * @param string $valuename
1092 * @return array
1093 */
1094function simple_hash_from_query($query, $keyname, $valuename)
1095{
1096  $array = array();
1097
1098  $result = pwg_query($query);
1099  while ($row = pwg_db_fetch_assoc($result))
1100  {
1101    $array[ $row[$keyname] ] = $row[$valuename];
1102  }
1103
1104  return $array;
1105}
1106
1107/**
1108 * creates an hashed based on a query, this function is a very common
1109 * pattern used here. The key is given as parameter, the value is an associative
1110 * array.
1111 *
1112 * @param string $query
1113 * @param string $keyname
1114 * @return array
1115 */
1116function hash_from_query($query, $keyname)
1117{
1118  $array = array();
1119  $result = pwg_query($query);
1120  while ($row = pwg_db_fetch_assoc($result))
1121  {
1122    $array[ $row[$keyname] ] = $row;
1123  }
1124  return $array;
1125}
1126
1127/**
1128 * Return basename of the current script
1129 * Lower case convertion is applied on return value
1130 * Return value is without file extention ".php"
1131 *
1132 * @param void
1133 *
1134 * @return script basename
1135 */
1136function script_basename()
1137{
1138  global $conf;
1139
1140  foreach (array('SCRIPT_NAME', 'SCRIPT_FILENAME', 'PHP_SELF') as $value)
1141  {
1142    if (!empty($_SERVER[$value]))
1143    {
1144      $filename = strtolower($_SERVER[$value]);
1145      if ($conf['php_extension_in_urls'] and get_extension($filename)!=='php')
1146        continue;
1147      $basename = basename($filename, '.php');
1148      if (!empty($basename))
1149      {
1150        return $basename;
1151      }
1152    }
1153  }
1154  return '';
1155}
1156
1157/**
1158 * Return value for the current page define on $conf['filter_pages']
1159 * Îf value is not defined, default value are returned
1160 *
1161 * @param value name
1162 *
1163 * @return filter page value
1164 */
1165function get_filter_page_value($value_name)
1166{
1167  global $conf;
1168
1169  $page_name = script_basename();
1170
1171  if (isset($conf['filter_pages'][$page_name][$value_name]))
1172  {
1173    return $conf['filter_pages'][$page_name][$value_name];
1174  }
1175  else if (isset($conf['filter_pages']['default'][$value_name]))
1176  {
1177    return $conf['filter_pages']['default'][$value_name];
1178  }
1179  else
1180  {
1181    return null;
1182  }
1183}
1184
1185/**
1186 * returns the character set of data sent to browsers / received from forms
1187 */
1188function get_pwg_charset()
1189{
1190  $pwg_charset = 'utf-8';
1191  if (defined('PWG_CHARSET'))
1192  {
1193    $pwg_charset = PWG_CHARSET;
1194  }
1195  return $pwg_charset;
1196}
1197
1198/**
1199 * includes a language file or returns the content of a language file
1200 * availability of the file
1201 *
1202 * in descending order of preference:
1203 *   param language, user language, default language
1204 * Piwigo default language.
1205 *
1206 * @param string filename
1207 * @param string dirname
1208 * @param mixed options can contain
1209 *     language - language to load (if empty uses user language)
1210 *     return - if true the file content is returned otherwise the file is evaluated as php
1211 *     target_charset -
1212 *     no_fallback - the language must be respected
1213 *     local - if true, get local language file
1214 * @return boolean success status or a string if options['return'] is true
1215 */
1216function load_language($filename, $dirname = '',
1217    $options = array() )
1218{
1219  global $user;
1220
1221  if (! @$options['return'] )
1222  {
1223    $filename .= '.php'; //MAYBE to do .. load .po and .mo localization files
1224  }
1225  if (empty($dirname))
1226  {
1227    $dirname = PHPWG_ROOT_PATH;
1228  }
1229  $dirname .= 'language/';
1230
1231  $languages = array();
1232  if ( !empty($options['language']) )
1233  {
1234    $languages[] = $options['language'];
1235  }
1236  if ( !empty($user['language']) )
1237  {
1238    $languages[] = $user['language'];
1239  }
1240  if ( ! @$options['no_fallback'] )
1241  {
1242    if ( defined('PHPWG_INSTALLED') )
1243    {
1244      $languages[] = get_default_language();
1245    }
1246    $languages[] = PHPWG_DEFAULT_LANGUAGE;
1247  }
1248
1249  $languages = array_unique($languages);
1250
1251  /*Note: target charset is always utf-8
1252  if ( empty($options['target_charset']) )
1253  {
1254    $target_charset = get_pwg_charset();
1255  }
1256  else
1257  {
1258    $target_charset = $options['target_charset'];
1259  }
1260  $target_charset = strtolower($target_charset);*/
1261  $source_file    = '';
1262  foreach ($languages as $language)
1263  {
1264    $f = @$options['local'] ?
1265      $dirname.$language.'.'.$filename:
1266      $dirname.$language.'/'.$filename;
1267
1268    if (file_exists($f))
1269    {
1270      $source_file = $f;
1271      break;
1272    }
1273  }
1274
1275  if ( !empty($source_file) )
1276  {
1277    if (! @$options['return'] )
1278    {
1279      @include($source_file);
1280      $load_lang = @$lang;
1281      $load_lang_info = @$lang_info;
1282
1283      global $lang, $lang_info;
1284      if ( !isset($lang) ) $lang=array();
1285      if ( !isset($lang_info) ) $lang_info=array();
1286
1287      /* Note: target charset is always utf-8
1288      if ( 'utf-8'!=$target_charset)
1289      {
1290        if ( is_array($load_lang) )
1291        {
1292          foreach ($load_lang as $k => $v)
1293          {
1294            if ( is_array($v) )
1295            {
1296              $func = create_function('$v', 'return convert_charset($v, "utf-8", "'.$target_charset.'");' );
1297              $lang[$k] = array_map($func, $v);
1298            }
1299            else
1300              $lang[$k] = convert_charset($v, 'utf-8', $target_charset);
1301          }
1302        }
1303        if ( is_array($load_lang_info) )
1304        {
1305          foreach ($load_lang_info as $k => $v)
1306          {
1307            $lang_info[$k] = convert_charset($v, 'utf-8', $target_charset);
1308          }
1309        }
1310      }
1311      else
1312      {*/
1313        $lang = array_merge( $lang, (array)$load_lang );
1314        $lang_info = array_merge( $lang_info, (array)$load_lang_info );
1315      //}
1316      return true;
1317    }
1318    else
1319    {
1320      $content = @file_get_contents($source_file);
1321      //Note: target charset is always utf-8 $content = convert_charset($content, 'utf-8', $target_charset);
1322      return $content;
1323    }
1324  }
1325  return false;
1326}
1327
1328/**
1329 * converts a string from a character set to another character set
1330 * @param string str the string to be converted
1331 * @param string source_charset the character set in which the string is encoded
1332 * @param string dest_charset the destination character set
1333 */
1334function convert_charset($str, $source_charset, $dest_charset)
1335{
1336  if ($source_charset==$dest_charset)
1337    return $str;
1338  if ($source_charset=='iso-8859-1' and $dest_charset=='utf-8')
1339  {
1340    return utf8_encode($str);
1341  }
1342  if ($source_charset=='utf-8' and $dest_charset=='iso-8859-1')
1343  {
1344    return utf8_decode($str);
1345  }
1346  if (function_exists('iconv'))
1347  {
1348    return iconv($source_charset, $dest_charset, $str);
1349  }
1350  if (function_exists('mb_convert_encoding'))
1351  {
1352    return mb_convert_encoding( $str, $dest_charset, $source_charset );
1353  }
1354  return $str; //???
1355}
1356
1357/**
1358 * makes sure a index.htm protects the directory from browser file listing
1359 *
1360 * @param string dir directory
1361 */
1362function secure_directory($dir)
1363{
1364  $file = $dir.'/index.htm';
1365  if (!file_exists($file))
1366  {
1367    @file_put_contents($file, 'Not allowed!');
1368  }
1369}
1370
1371/**
1372 * returns a "secret key" that is to be sent back when a user posts a form
1373 *
1374 * @param int valid_after_seconds - key validity start time from now
1375 */
1376function get_ephemeral_key($valid_after_seconds, $aditionnal_data_to_hash = '')
1377{
1378        global $conf;
1379        $time = round(microtime(true), 1);
1380        return $time.':'.$valid_after_seconds.':'
1381                .hash_hmac(
1382                        'md5',
1383                        $time.substr($_SERVER['REMOTE_ADDR'],0,5).$valid_after_seconds.$aditionnal_data_to_hash,
1384                        $conf['secret_key']);
1385}
1386
1387function verify_ephemeral_key($key, $aditionnal_data_to_hash = '')
1388{
1389        global $conf;
1390        $time = microtime(true);
1391        $key = explode( ':', @$key );
1392        if ( count($key)!=3
1393                or $key[0]>$time-(float)$key[1] // page must have been retrieved more than X sec ago
1394                or $key[0]<$time-3600 // 60 minutes expiration
1395                or hash_hmac(
1396                          'md5', $key[0].substr($_SERVER['REMOTE_ADDR'],0,5).$key[1].$aditionnal_data_to_hash, $conf['secret_key']
1397                        ) != $key[2]
1398          )
1399        {
1400                return false;
1401        }
1402        return true;
1403}
1404
1405/**
1406 * return an array which will be sent to template to display navigation bar
1407 */
1408function create_navigation_bar($url, $nb_element, $start, $nb_element_page, $clean_url = false, $param_name='start')
1409{
1410  global $conf;
1411
1412  $navbar = array();
1413  $pages_around = $conf['paginate_pages_around'];
1414  $start_str = $clean_url ? '/'.$param_name.'-' : (strpos($url, '?')===false ? '?':'&amp;').$param_name.'=';
1415
1416  if (!isset($start) or !is_numeric($start) or (is_numeric($start) and $start < 0))
1417  {
1418    $start = 0;
1419  }
1420
1421  // navigation bar useful only if more than one page to display !
1422  if ($nb_element > $nb_element_page)
1423  {
1424    $url_start = $url.$start_str;
1425
1426    $cur_page = $navbar['CURRENT_PAGE'] = $start / $nb_element_page + 1;
1427    $maximum = ceil($nb_element / $nb_element_page);
1428
1429    $start = $nb_element_page * round( $start / $nb_element_page );
1430    $previous = $start - $nb_element_page;
1431    $next = $start + $nb_element_page;
1432    $last = ($maximum - 1) * $nb_element_page;
1433
1434    // link to first page and previous page?
1435    if ($cur_page != 1)
1436    {
1437      $navbar['URL_FIRST'] = $url;
1438      $navbar['URL_PREV'] = $previous > 0 ? $url_start.$previous : $url;
1439    }
1440    // link on next page and last page?
1441    if ($cur_page != $maximum)
1442    {
1443      $navbar['URL_NEXT'] = $url_start.($next < $last ? $next : $last);
1444      $navbar['URL_LAST'] = $url_start.$last;
1445    }
1446
1447    // pages to display
1448    $navbar['pages'] = array();
1449    $navbar['pages'][1] = $url;
1450    for ($i = max( floor($cur_page) - $pages_around , 2), $stop = min( ceil($cur_page) + $pages_around + 1, $maximum);
1451         $i < $stop; $i++)
1452    {
1453      $navbar['pages'][$i] = $url.$start_str.(($i - 1) * $nb_element_page);
1454    }
1455    $navbar['pages'][$maximum] = $url_start.$last;
1456  }
1457  return $navbar;
1458}
1459
1460/**
1461 * return an array which will be sent to template to display recent icon
1462 */
1463function get_icon($date, $is_child_date = false)
1464{
1465  global $cache, $user;
1466
1467  if (empty($date))
1468  {
1469    return false;
1470  }
1471
1472  if (!isset($cache['get_icon']['title']))
1473  {
1474    $cache['get_icon']['title'] = sprintf(
1475      l10n('photos posted during the last %d days'),
1476      $user['recent_period']
1477      );
1478  }
1479
1480  $icon = array(
1481    'TITLE' => $cache['get_icon']['title'],
1482    'IS_CHILD_DATE' => $is_child_date,
1483    );
1484
1485  if (isset($cache['get_icon'][$date]))
1486  {
1487    return $cache['get_icon'][$date] ? $icon : array();
1488  }
1489
1490  if (!isset($cache['get_icon']['sql_recent_date']))
1491  {
1492    // Use MySql date in order to standardize all recent "actions/queries"
1493    $cache['get_icon']['sql_recent_date'] = pwg_db_get_recent_period($user['recent_period']);
1494  }
1495
1496  $cache['get_icon'][$date] = $date > $cache['get_icon']['sql_recent_date'];
1497
1498  return $cache['get_icon'][$date] ? $icon : array();
1499}
1500
1501/**
1502 * check token comming from form posted or get params to prevent csrf attacks
1503 * if pwg_token is empty action doesn't require token
1504 * else pwg_token is compare to server token
1505 *
1506 * @return void access denied if token given is not equal to server token
1507 */
1508function check_pwg_token()
1509{
1510  if (!empty($_REQUEST['pwg_token']))
1511  {
1512    if (get_pwg_token() != $_REQUEST['pwg_token'])
1513    {
1514      access_denied();
1515    }
1516  }
1517  else
1518    bad_request('missing token');
1519}
1520
1521function get_pwg_token()
1522{
1523  global $conf;
1524
1525  return hash_hmac('md5', session_id(), $conf['secret_key']);
1526}
1527
1528/*
1529 * breaks the script execution if the given value doesn't match the given
1530 * pattern. This should happen only during hacking attempts.
1531 *
1532 * @param string param_name
1533 * @param array param_array
1534 * @param boolean is_array
1535 * @param string pattern
1536 *
1537 * @return void
1538 */
1539function check_input_parameter($param_name, $param_array, $is_array, $pattern)
1540{
1541  $param_value = null;
1542  if (isset($param_array[$param_name]))
1543  {
1544    $param_value = $param_array[$param_name];
1545  }
1546
1547  // it's ok if the input parameter is null
1548  if (empty($param_value))
1549  {
1550    return true;
1551  }
1552
1553  if ($is_array)
1554  {
1555    if (!is_array($param_value))
1556    {
1557      fatal_error('[Hacking attempt] the input parameter "'.$param_name.'" should be an array');
1558    }
1559
1560    foreach ($param_value as $item_to_check)
1561    {
1562      if (!preg_match($pattern, $item_to_check))
1563      {
1564        fatal_error('[Hacking attempt] an item is not valid in input parameter "'.$param_name.'"');
1565      }
1566    }
1567  }
1568  else
1569  {
1570    if (!preg_match($pattern, $param_value))
1571    {
1572      fatal_error('[Hacking attempt] the input parameter "'.$param_name.'" is not valid');
1573    }
1574  }
1575}
1576
1577
1578function get_privacy_level_options()
1579{
1580  global $conf;
1581
1582  $options = array();
1583  $label = '';
1584  foreach (array_reverse($conf['available_permission_levels']) as $level)
1585  {
1586    if (0 == $level)
1587    {
1588      $label = l10n('Everybody');
1589    }
1590    else
1591    {
1592      if (strlen($label))
1593      {
1594        $label .= ', ';
1595      }
1596      $label .= l10n( sprintf('Level %d',$level) );
1597    }
1598    $options[$level] = $label;
1599  }
1600  return $options;
1601}
1602
1603
1604/**
1605 * return the branch from the version. For example version 2.2.4 is for branch 2.2
1606 */
1607function get_branch_from_version($version)
1608{
1609  return implode('.', array_slice(explode('.', $version), 0, 2));
1610}
1611
1612/**
1613 * return the device type: mobile, tablet or desktop
1614 */
1615function get_device()
1616{
1617  $device = pwg_get_session_var('device');
1618
1619  if (is_null($device))
1620  {
1621    include_once(PHPWG_ROOT_PATH.'include/mdetect.php');
1622    $uagent_obj = new uagent_info();
1623    if ($uagent_obj->DetectTierIphone())
1624    {
1625      $device = 'mobile';
1626    }
1627    elseif ($uagent_obj->DetectTierTablet())
1628    {
1629      $device = 'tablet';
1630    }
1631    else
1632    {
1633      $device = 'desktop';
1634    }
1635    pwg_set_session_var('device', $device);
1636  }
1637
1638  return $device;
1639}
1640
1641/**
1642 * return true if mobile theme should be loaded
1643 */
1644function mobile_theme()
1645{
1646  global $conf;
1647
1648  if (empty($conf['mobile_theme']))
1649  {
1650    return false;
1651  }
1652
1653  if (isset($_GET['mobile']))
1654  {
1655    $is_mobile_theme = get_boolean($_GET['mobile']);
1656    pwg_set_session_var('mobile_theme', $is_mobile_theme);
1657    unset($_GET['mobile']);
1658  }
1659  else
1660  {
1661    $is_mobile_theme = pwg_get_session_var('mobile_theme');
1662  }
1663
1664  if (is_null($is_mobile_theme))
1665  {
1666    $is_mobile_theme = (get_device() == 'mobile');
1667    pwg_set_session_var('mobile_theme', $is_mobile_theme);
1668  }
1669
1670  return $is_mobile_theme;
1671}
1672
1673/**
1674 * check url format
1675 */
1676function url_check_format($url)
1677{
1678  if (version_compare(PHP_VERSION, '5.2.0') >= 0)
1679  {
1680    return filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)!==false;
1681  }
1682  else
1683  {
1684    // http://mathiasbynens.be/demo/url-regex @imme_emosol
1685    return (bool)preg_match('@^https?://(-\.)?([^\s/?\.#-]+\.?)+(/[^\s]*)?$@iS', $url);
1686  }
1687}
1688
1689/**
1690 * check email format
1691 */
1692function email_check_format($mail_address)
1693{
1694  if (version_compare(PHP_VERSION, '5.2.0') >= 0)
1695  {
1696    return filter_var($mail_address, FILTER_VALIDATE_EMAIL)!==false;
1697  }
1698  else
1699  {
1700    $atom   = '[-a-z0-9!#$%&\'*+\\/=?^_`{|}~]';   // before  arobase
1701    $domain = '([a-z0-9]([-a-z0-9]*[a-z0-9]+)?)'; // domain name
1702    $regex = '/^' . $atom . '+' . '(\.' . $atom . '+)*' . '@' . '(' . $domain . '{1,63}\.)+' . $domain . '{2,63}$/i';
1703
1704    return (bool)preg_match($regex, $mail_address);
1705  }
1706}
1707?>
Note: See TracBrowser for help on using the repository browser.