source: tags/2.4.5/include/functions.inc.php @ 27558

Last change on this file since 27558 was 17749, checked in by rvelices, 12 years ago

merge -r17748 from trunk to branch 2.4 bug 2735: fix/improve non latin language tags

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