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

Revision 17748, 42.9 KB checked in by rvelices, 7 years ago (diff)

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
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/**
840 * fill the current user caddie with given elements, if not already in
841 * caddie
842 *
843 * @param array elements_id
844 */
845function fill_caddie($elements_id)
846{
847  global $user;
848
849  $query = '
850SELECT element_id
851  FROM '.CADDIE_TABLE.'
852  WHERE user_id = '.$user['id'].'
853;';
854  $in_caddie = array_from_query($query, 'element_id');
855
856  $caddiables = array_diff($elements_id, $in_caddie);
857
858  $datas = array();
859
860  foreach ($caddiables as $caddiable)
861  {
862    array_push($datas, array('element_id' => $caddiable,
863                             'user_id' => $user['id']));
864  }
865
866  if (count($caddiables) > 0)
867  {
868    mass_inserts(CADDIE_TABLE, array('element_id','user_id'), $datas);
869  }
870}
871
872/**
873 * returns the element name from its filename
874 *
875 * @param string filename
876 * @return string name
877 */
878function get_name_from_file($filename)
879{
880  return str_replace('_',' ',get_filename_wo_extension($filename));
881}
882
883/**
884 * returns the corresponding value from $lang if existing. Else, the key is
885 * returned
886 *
887 * @param string key
888 * @return string
889 */
890function l10n($key)
891{
892  global $lang, $conf;
893
894  if ( ($val=@$lang[$key]) == null)
895  {
896    if ($conf['debug_l10n'] and !isset($lang[$key]) and !empty($key))
897    {
898      trigger_error('[l10n] language key "'.$key.'" is not defined', E_USER_WARNING);
899    }
900    $val = $key;
901  }
902  return $val;
903}
904
905/**
906 * returns the prinft value for strings including %d
907 * return is concorded with decimal value (singular, plural)
908 *
909 * @param singular string key
910 * @param plural string key
911 * @param decimal value
912 * @return string
913 */
914function l10n_dec($singular_fmt_key, $plural_fmt_key, $decimal)
915{
916  global $lang_info;
917
918  return
919    sprintf(
920      l10n((
921        (($decimal > 1) or ($decimal == 0 and $lang_info['zero_plural']))
922          ? $plural_fmt_key
923          : $singular_fmt_key
924        )), $decimal);
925}
926
927/*
928 * returns a single element to use with l10n_args
929 *
930 * @param string key: translation key
931 * @param array/string/../number args:
932 *   arguments to use on sprintf($key, args)
933 *   if args is a array, each values are used on sprintf
934 * @return string
935 */
936function get_l10n_args($key, $args)
937{
938  if (is_array($args))
939  {
940    $key_arg = array_merge(array($key), $args);
941  }
942  else
943  {
944    $key_arg = array($key,  $args);
945  }
946  return array('key_args' => $key_arg);
947}
948
949/*
950 * returns a string with formated with l10n_args elements
951 *
952 * @param element/array $key_args: element or array of l10n_args elements
953 * @param $sep: if $key_args is array,
954 *   separator is used when translated l10n_args elements are concated
955 * @return string
956 */
957function l10n_args($key_args, $sep = "\n")
958{
959  if (is_array($key_args))
960  {
961    foreach ($key_args as $key => $element)
962    {
963      if (isset($result))
964      {
965        $result .= $sep;
966      }
967      else
968      {
969        $result = '';
970      }
971
972      if ($key === 'key_args')
973      {
974        array_unshift($element, l10n(array_shift($element)));
975        $result .= call_user_func_array('sprintf', $element);
976      }
977      else
978      {
979        $result .= l10n_args($element, $sep);
980      }
981    }
982  }
983  else
984  {
985    fatal_error('l10n_args: Invalid arguments');
986  }
987
988  return $result;
989}
990
991/**
992 * returns the corresponding value from $themeconf if existing. Else, the
993 * key is returned
994 *
995 * @param string key
996 * @return string
997 */
998function get_themeconf($key)
999{
1000  global $template;
1001
1002  return $template->get_themeconf($key);
1003}
1004
1005/**
1006 * Returns webmaster mail address depending on $conf['webmaster_id']
1007 *
1008 * @return string
1009 */
1010function get_webmaster_mail_address()
1011{
1012  global $conf;
1013
1014  $query = '
1015SELECT '.$conf['user_fields']['email'].'
1016  FROM '.USERS_TABLE.'
1017  WHERE '.$conf['user_fields']['id'].' = '.$conf['webmaster_id'].'
1018;';
1019  list($email) = pwg_db_fetch_row(pwg_query($query));
1020
1021  return $email;
1022}
1023
1024/**
1025 * Add configuration parameters from database to global $conf array
1026 *
1027 * @return void
1028 */
1029function load_conf_from_db($condition = '')
1030{
1031  global $conf;
1032
1033  $query = '
1034SELECT param, value
1035 FROM '.CONFIG_TABLE.'
1036 '.(!empty($condition) ? 'WHERE '.$condition : '').'
1037;';
1038  $result = pwg_query($query);
1039
1040  if ((pwg_db_num_rows($result) == 0) and !empty($condition))
1041  {
1042    fatal_error('No configuration data');
1043  }
1044
1045  while ($row = pwg_db_fetch_assoc($result))
1046  {
1047    $val = isset($row['value']) ? $row['value'] : '';
1048    // If the field is true or false, the variable is transformed into a boolean value.
1049    if ($val == 'true')
1050    {
1051      $val = true;
1052    }
1053    elseif ($val == 'false')
1054    {
1055      $val = false;
1056    }
1057    $conf[ $row['param'] ] = $val;
1058  }
1059}
1060
1061function conf_update_param($param, $value)
1062{
1063  $query = '
1064SELECT
1065    param,
1066    value
1067  FROM '.CONFIG_TABLE.'
1068  WHERE param = \''.$param.'\'
1069;';
1070  $params = array_from_query($query, 'param');
1071
1072  if (count($params) == 0)
1073  {
1074    $query = '
1075INSERT
1076  INTO '.CONFIG_TABLE.'
1077  (param, value)
1078  VALUES(\''.$param.'\', \''.$value.'\')
1079;';
1080    pwg_query($query);
1081  }
1082  else
1083  {
1084    $query = '
1085UPDATE '.CONFIG_TABLE.'
1086  SET value = \''.$value.'\'
1087  WHERE param = \''.$param.'\'
1088;';
1089    pwg_query($query);
1090  }
1091}
1092
1093/**
1094 * Prepends and appends a string at each value of the given array.
1095 *
1096 * @param array
1097 * @param string prefix to each array values
1098 * @param string suffix to each array values
1099 */
1100function prepend_append_array_items($array, $prepend_str, $append_str)
1101{
1102  array_walk(
1103    $array,
1104    create_function('&$s', '$s = "'.$prepend_str.'".$s."'.$append_str.'";')
1105    );
1106
1107  return $array;
1108}
1109
1110/**
1111 * creates an hashed based on a query, this function is a very common
1112 * pattern used here. Among the selected columns fetched, choose one to be
1113 * the key, another one to be the value.
1114 *
1115 * @param string $query
1116 * @param string $keyname
1117 * @param string $valuename
1118 * @return array
1119 */
1120function simple_hash_from_query($query, $keyname, $valuename)
1121{
1122  $array = array();
1123
1124  $result = pwg_query($query);
1125  while ($row = pwg_db_fetch_assoc($result))
1126  {
1127    $array[ $row[$keyname] ] = $row[$valuename];
1128  }
1129
1130  return $array;
1131}
1132
1133/**
1134 * creates an hashed based on a query, this function is a very common
1135 * pattern used here. The key is given as parameter, the value is an associative
1136 * array.
1137 *
1138 * @param string $query
1139 * @param string $keyname
1140 * @return array
1141 */
1142function hash_from_query($query, $keyname)
1143{
1144  $array = array();
1145  $result = pwg_query($query);
1146  while ($row = pwg_db_fetch_assoc($result))
1147  {
1148    $array[ $row[$keyname] ] = $row;
1149  }
1150  return $array;
1151}
1152
1153/**
1154 * Return basename of the current script
1155 * Lower case convertion is applied on return value
1156 * Return value is without file extention ".php"
1157 *
1158 * @param void
1159 *
1160 * @return script basename
1161 */
1162function script_basename()
1163{
1164  global $conf;
1165
1166  foreach (array('SCRIPT_NAME', 'SCRIPT_FILENAME', 'PHP_SELF') as $value)
1167  {
1168    if (!empty($_SERVER[$value]))
1169    {
1170      $filename = strtolower($_SERVER[$value]);
1171      if ($conf['php_extension_in_urls'] and get_extension($filename)!=='php')
1172        continue;
1173      $basename = basename($filename, '.php');
1174      if (!empty($basename))
1175      {
1176        return $basename;
1177      }
1178    }
1179  }
1180  return '';
1181}
1182
1183/**
1184 * Return value for the current page define on $conf['filter_pages']
1185 * Îf value is not defined, default value are returned
1186 *
1187 * @param value name
1188 *
1189 * @return filter page value
1190 */
1191function get_filter_page_value($value_name)
1192{
1193  global $conf;
1194
1195  $page_name = script_basename();
1196
1197  if (isset($conf['filter_pages'][$page_name][$value_name]))
1198  {
1199    return $conf['filter_pages'][$page_name][$value_name];
1200  }
1201  else if (isset($conf['filter_pages']['default'][$value_name]))
1202  {
1203    return $conf['filter_pages']['default'][$value_name];
1204  }
1205  else
1206  {
1207    return null;
1208  }
1209}
1210
1211/**
1212 * returns the character set of data sent to browsers / received from forms
1213 */
1214function get_pwg_charset()
1215{
1216  $pwg_charset = 'utf-8';
1217  if (defined('PWG_CHARSET'))
1218  {
1219    $pwg_charset = PWG_CHARSET;
1220  }
1221  return $pwg_charset;
1222}
1223
1224/**
1225 * includes a language file or returns the content of a language file
1226 * availability of the file
1227 *
1228 * in descending order of preference:
1229 *   param language, user language, default language
1230 * Piwigo default language.
1231 *
1232 * @param string filename
1233 * @param string dirname
1234 * @param mixed options can contain
1235 *     language - language to load (if empty uses user language)
1236 *     return - if true the file content is returned otherwise the file is evaluated as php
1237 *     target_charset -
1238 *     no_fallback - the language must be respected
1239 *     local - if true, get local language file
1240 * @return boolean success status or a string if options['return'] is true
1241 */
1242function load_language($filename, $dirname = '',
1243    $options = array() )
1244{
1245  global $user;
1246
1247  if (! @$options['return'] )
1248  {
1249    $filename .= '.php'; //MAYBE to do .. load .po and .mo localization files
1250  }
1251  if (empty($dirname))
1252  {
1253    $dirname = PHPWG_ROOT_PATH;
1254  }
1255  $dirname .= 'language/';
1256
1257  $languages = array();
1258  if ( !empty($options['language']) )
1259  {
1260    $languages[] = $options['language'];
1261  }
1262  if ( !empty($user['language']) )
1263  {
1264    $languages[] = $user['language'];
1265  }
1266  if ( ! @$options['no_fallback'] )
1267  {
1268    if ( defined('PHPWG_INSTALLED') )
1269    {
1270      $languages[] = get_default_language();
1271    }
1272    $languages[] = PHPWG_DEFAULT_LANGUAGE;
1273  }
1274
1275  $languages = array_unique($languages);
1276
1277  if ( empty($options['target_charset']) )
1278  {
1279    $target_charset = get_pwg_charset();
1280  }
1281  else
1282  {
1283    $target_charset = $options['target_charset'];
1284  }
1285  $target_charset = strtolower($target_charset);
1286  $source_file    = '';
1287  foreach ($languages as $language)
1288  {
1289    $f = @$options['local'] ?
1290      $dirname.$language.'.'.$filename:
1291      $dirname.$language.'/'.$filename;
1292
1293    if (file_exists($f))
1294    {
1295      $source_file = $f;
1296      break;
1297    }
1298  }
1299
1300  if ( !empty($source_file) )
1301  {
1302    if (! @$options['return'] )
1303    {
1304      @include($source_file);
1305      $load_lang = @$lang;
1306      $load_lang_info = @$lang_info;
1307
1308      global $lang, $lang_info;
1309      if ( !isset($lang) ) $lang=array();
1310      if ( !isset($lang_info) ) $lang_info=array();
1311
1312      if ( 'utf-8'!=$target_charset)
1313      {
1314        if ( is_array($load_lang) )
1315        {
1316          foreach ($load_lang as $k => $v)
1317          {
1318            if ( is_array($v) )
1319            {
1320              $func = create_function('$v', 'return convert_charset($v, "utf-8", "'.$target_charset.'");' );
1321              $lang[$k] = array_map($func, $v);
1322            }
1323            else
1324              $lang[$k] = convert_charset($v, 'utf-8', $target_charset);
1325          }
1326        }
1327        if ( is_array($load_lang_info) )
1328        {
1329          foreach ($load_lang_info as $k => $v)
1330          {
1331            $lang_info[$k] = convert_charset($v, 'utf-8', $target_charset);
1332          }
1333        }
1334      }
1335      else
1336      {
1337        $lang = array_merge( $lang, (array)$load_lang );
1338        $lang_info = array_merge( $lang_info, (array)$load_lang_info );
1339      }
1340      return true;
1341    }
1342    else
1343    {
1344      $content = @file_get_contents($source_file);
1345      $content = convert_charset($content, 'utf-8', $target_charset);
1346      return $content;
1347    }
1348  }
1349  return false;
1350}
1351
1352/**
1353 * converts a string from a character set to another character set
1354 * @param string str the string to be converted
1355 * @param string source_charset the character set in which the string is encoded
1356 * @param string dest_charset the destination character set
1357 */
1358function convert_charset($str, $source_charset, $dest_charset)
1359{
1360  if ($source_charset==$dest_charset)
1361    return $str;
1362  if ($source_charset=='iso-8859-1' and $dest_charset=='utf-8')
1363  {
1364    return utf8_encode($str);
1365  }
1366  if ($source_charset=='utf-8' and $dest_charset=='iso-8859-1')
1367  {
1368    return utf8_decode($str);
1369  }
1370  if (function_exists('iconv'))
1371  {
1372    return iconv($source_charset, $dest_charset, $str);
1373  }
1374  if (function_exists('mb_convert_encoding'))
1375  {
1376    return mb_convert_encoding( $str, $dest_charset, $source_charset );
1377  }
1378  return $str; //???
1379}
1380
1381/**
1382 * makes sure a index.htm protects the directory from browser file listing
1383 *
1384 * @param string dir directory
1385 */
1386function secure_directory($dir)
1387{
1388  $file = $dir.'/index.htm';
1389  if (!file_exists($file))
1390  {
1391    @file_put_contents($file, 'Not allowed!');
1392  }
1393}
1394
1395/**
1396 * returns a "secret key" that is to be sent back when a user posts a form
1397 *
1398 * @param int valid_after_seconds - key validity start time from now
1399 */
1400function get_ephemeral_key($valid_after_seconds, $aditionnal_data_to_hash = '')
1401{
1402        global $conf;
1403        $time = round(microtime(true), 1);
1404        return $time.':'.$valid_after_seconds.':'
1405                .hash_hmac(
1406                        'md5',
1407                        $time.substr($_SERVER['REMOTE_ADDR'],0,5).$valid_after_seconds.$aditionnal_data_to_hash,
1408                        $conf['secret_key']);
1409}
1410
1411function verify_ephemeral_key($key, $aditionnal_data_to_hash = '')
1412{
1413        global $conf;
1414        $time = microtime(true);
1415        $key = explode( ':', @$key );
1416        if ( count($key)!=3
1417                or $key[0]>$time-(float)$key[1] // page must have been retrieved more than X sec ago
1418                or $key[0]<$time-3600 // 60 minutes expiration
1419                or hash_hmac(
1420                          'md5', $key[0].substr($_SERVER['REMOTE_ADDR'],0,5).$key[1].$aditionnal_data_to_hash, $conf['secret_key']
1421                        ) != $key[2]
1422          )
1423        {
1424                return false;
1425        }
1426        return true;
1427}
1428
1429/**
1430 * return an array which will be sent to template to display navigation bar
1431 */
1432function create_navigation_bar($url, $nb_element, $start, $nb_element_page, $clean_url = false)
1433{
1434  global $conf;
1435
1436  $navbar = array();
1437  $pages_around = $conf['paginate_pages_around'];
1438  $start_str = $clean_url ? '/start-' : (strpos($url, '?')===false ? '?':'&amp;').'start=';
1439
1440  if (!isset($start) or !is_numeric($start) or (is_numeric($start) and $start < 0))
1441  {
1442    $start = 0;
1443  }
1444
1445  // navigation bar useful only if more than one page to display !
1446  if ($nb_element > $nb_element_page)
1447  {
1448    $cur_page = ceil($start / $nb_element_page) + 1;
1449    $maximum = ceil($nb_element / $nb_element_page);
1450    $previous = $start - $nb_element_page;
1451    $next = $start + $nb_element_page;
1452    $last = ($maximum - 1) * $nb_element_page;
1453
1454    $navbar['CURRENT_PAGE'] = $cur_page;
1455
1456    // link to first page and previous page?
1457    if ($cur_page != 1)
1458    {
1459      $navbar['URL_FIRST'] = $url;
1460      $navbar['URL_PREV'] = $url.($previous > 0 ? $start_str.$previous : '');
1461    }
1462    // link on next page and last page?
1463    if ($cur_page != $maximum)
1464    {
1465      $navbar['URL_NEXT'] = $url.$start_str.$next;
1466      $navbar['URL_LAST'] = $url.$start_str.$last;
1467    }
1468
1469    // pages to display
1470    $navbar['pages'] = array();
1471    $navbar['pages'][1] = $url;
1472    $navbar['pages'][$maximum] = $url.$start_str.$last;
1473
1474    for ($i = max($cur_page - $pages_around , 2), $stop = min($cur_page + $pages_around + 1, $maximum);
1475         $i < $stop; $i++)
1476    {
1477      $navbar['pages'][$i] = $url.$start_str.(($i - 1) * $nb_element_page);
1478    }
1479    ksort($navbar['pages']);
1480  }
1481  return $navbar;
1482}
1483
1484/**
1485 * return an array which will be sent to template to display recent icon
1486 */
1487function get_icon($date, $is_child_date = false)
1488{
1489  global $cache, $user;
1490
1491  if (empty($date))
1492  {
1493    return false;
1494  }
1495
1496  if (!isset($cache['get_icon']['title']))
1497  {
1498    $cache['get_icon']['title'] = sprintf(
1499      l10n('photos posted during the last %d days'),
1500      $user['recent_period']
1501      );
1502  }
1503
1504  $icon = array(
1505    'TITLE' => $cache['get_icon']['title'],
1506    'IS_CHILD_DATE' => $is_child_date,
1507    );
1508
1509  if (isset($cache['get_icon'][$date]))
1510  {
1511    return $cache['get_icon'][$date] ? $icon : array();
1512  }
1513
1514  if (!isset($cache['get_icon']['sql_recent_date']))
1515  {
1516    // Use MySql date in order to standardize all recent "actions/queries"
1517    $cache['get_icon']['sql_recent_date'] = pwg_db_get_recent_period($user['recent_period']);
1518  }
1519
1520  $cache['get_icon'][$date] = $date > $cache['get_icon']['sql_recent_date'];
1521
1522  return $cache['get_icon'][$date] ? $icon : array();
1523}
1524
1525/**
1526 * check token comming from form posted or get params to prevent csrf attacks
1527 * if pwg_token is empty action doesn't require token
1528 * else pwg_token is compare to server token
1529 *
1530 * @return void access denied if token given is not equal to server token
1531 */
1532function check_pwg_token()
1533{
1534  if (!empty($_REQUEST['pwg_token']))
1535  {
1536    if (get_pwg_token() != $_REQUEST['pwg_token'])
1537    {
1538      access_denied();
1539    }
1540  }
1541  else
1542    bad_request('missing token');
1543}
1544
1545function get_pwg_token()
1546{
1547  global $conf;
1548
1549  return hash_hmac('md5', session_id(), $conf['secret_key']);
1550}
1551
1552/*
1553 * breaks the script execution if the given value doesn't match the given
1554 * pattern. This should happen only during hacking attempts.
1555 *
1556 * @param string param_name
1557 * @param array param_array
1558 * @param boolean is_array
1559 * @param string pattern
1560 *
1561 * @return void
1562 */
1563function check_input_parameter($param_name, $param_array, $is_array, $pattern)
1564{
1565  $param_value = null;
1566  if (isset($param_array[$param_name]))
1567  {
1568    $param_value = $param_array[$param_name];
1569  }
1570
1571  // it's ok if the input parameter is null
1572  if (empty($param_value))
1573  {
1574    return true;
1575  }
1576
1577  if ($is_array)
1578  {
1579    if (!is_array($param_value))
1580    {
1581      fatal_error('[Hacking attempt] the input parameter "'.$param_name.'" should be an array');
1582    }
1583
1584    foreach ($param_value as $item_to_check)
1585    {
1586      if (!preg_match($pattern, $item_to_check))
1587      {
1588        fatal_error('[Hacking attempt] an item is not valid in input parameter "'.$param_name.'"');
1589      }
1590    }
1591  }
1592  else
1593  {
1594    if (!preg_match($pattern, $param_value))
1595    {
1596      fatal_error('[Hacking attempt] the input parameter "'.$param_name.'" is not valid');
1597    }
1598  }
1599}
1600
1601
1602function get_privacy_level_options()
1603{
1604  global $conf;
1605
1606  $options = array();
1607  foreach (array_reverse($conf['available_permission_levels']) as $level)
1608  {
1609    $label = null;
1610
1611    if (0 == $level)
1612    {
1613      $label = l10n('Everybody');
1614    }
1615    else
1616    {
1617      $labels = array();
1618      $sub_levels = array_reverse($conf['available_permission_levels']);
1619      foreach ($sub_levels as $sub_level)
1620      {
1621        if ($sub_level == 0 or $sub_level < $level)
1622        {
1623          break;
1624        }
1625        array_push(
1626          $labels,
1627          l10n(
1628            sprintf(
1629              'Level %d',
1630              $sub_level
1631              )
1632            )
1633          );
1634      }
1635
1636      $label = implode(', ', $labels);
1637    }
1638    $options[$level] = $label;
1639  }
1640  return $options;
1641}
1642
1643
1644/**
1645 * return the branch from the version. For example version 2.2.4 is for branch 2.2
1646 */
1647function get_branch_from_version($version)
1648{
1649  return implode('.', array_slice(explode('.', $version), 0, 2));
1650}
1651
1652/**
1653 * return the device type: mobile, tablet or desktop
1654 */
1655function get_device()
1656{
1657  $device = pwg_get_session_var('device');
1658
1659  if (is_null($device))
1660  {
1661    include_once(PHPWG_ROOT_PATH.'include/mdetect.php');
1662    $uagent_obj = new uagent_info();
1663    if ($uagent_obj->DetectTierIphone())
1664    {
1665      $device = 'mobile';
1666    }
1667    elseif ($uagent_obj->DetectTierTablet())
1668    {
1669      $device = 'tablet';
1670    }
1671    else
1672    {
1673      $device = 'desktop';
1674    }
1675    pwg_set_session_var('device', $device);
1676  }
1677
1678  return $device;
1679}
1680
1681/**
1682 * return true if mobile theme should be loaded
1683 */
1684function mobile_theme()
1685{
1686  global $conf;
1687
1688  if (empty($conf['mobile_theme']))
1689  {
1690    return false;
1691  }
1692
1693  if (isset($_GET['mobile']))
1694  {
1695    $is_mobile_theme = get_boolean($_GET['mobile']);
1696    pwg_set_session_var('mobile_theme', $is_mobile_theme);
1697    unset($_GET['mobile']);
1698  }
1699  else
1700  {
1701    $is_mobile_theme = pwg_get_session_var('mobile_theme');
1702  }
1703
1704  if (is_null($is_mobile_theme))
1705  {
1706    $is_mobile_theme = (get_device() == 'mobile');
1707    pwg_set_session_var('mobile_theme', $is_mobile_theme);
1708  }
1709
1710  return $is_mobile_theme;
1711}
1712
1713/**
1714 * check url format
1715 */
1716function url_check_format($url)
1717{
1718  if (version_compare(PHP_VERSION, '5.2.0') >= 0)
1719  {
1720    return filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)!==false;
1721  }
1722  else
1723  {
1724    // http://mathiasbynens.be/demo/url-regex @imme_emosol
1725    return (bool)preg_match('@^https?://(-\.)?([^\s/?\.#-]+\.?)+(/[^\s]*)?$@iS', $url);
1726  }
1727}
1728?>
Note: See TracBrowser for help on using the repository browser.