root / phpfspot.class.php

View | Annotate | Download (118.3 KB)

1
<?php
2
3
/***************************************************************************
4
 *
5
 * phpfspot, presents your F-Spot photo collection in Web browsers.
6
 *
7
 * Copyright (c) by Andreas Unterkircher
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; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program; if not, write to the Free Software
21
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
 *
23
 ***************************************************************************/
24
25
require_once "phpfspot_cfg.php";
26
require_once "phpfspot_db.php";
27
28
/**
29
 * PHPFSPOT main class
30
 *
31
 * this class contains the most functions which will to the major
32
 * work for phpfspot.
33
 *
34
 * @package phpfspot
35
 */
36
class PHPFSPOT {
37
38
   /**
39
     * phpfspot configuration
40
     * @access public
41
     * @see PHPFSPOT_CFG()
42
     * @var PHPFSPOT_CFG
43
     */
44
   var $cfg;
45
46
   /**
47
     * SQLite database handle to f-spot database
48
     * @see PHPFSPOT_DB()
49
     * @access public
50
     * @var PHPFSPOT_DB
51
     */
52
   var $db;
53
54
   /**
55
     * SQLite database handle to phpfspot database
56
     * @see PHPFSPOT_DB()
57
     * @access public
58
     * @var PHPFSPOT_DB
59
     */
60
   var $cfg_db;
61
62
   /**
63
    * Smarty template engine
64
    * @link http://smarty.php.net smarty.php.net
65
    * @see PHPFSPOT_TMPL()
66
    * @access public
67
    * @var PHPFSPOT_TMPL
68
    */
69
   var $tmpl;
70
71
   /**
72
    * full tag - list
73
    * @access public
74
    * @var array
75
    */
76
   var $tags;
77
78
   /**
79
    * list of available, not-selected, tags
80
    * @access public
81
    * @var array
82
    */
83
   var $avail_tags;
84
85
   /**
86
    * true if runtime error occued
87
    * @access private
88
    * @var boolean
89
    */
90
   private $runtime_error = false;
91
92
   /**
93
    * F-Spot database version
94
    * @access private
95
    * @var integer
96
    */
97
   private $dbver;
98
99
   /**
100
    * class constructor ($cfg, $db, $cfg_db, $tmpl, $db_ver)
101
    *
102
    * this function will be called on class construct
103
    * and will check requirements, loads configuration,
104
    * open databases and start the user session
105
    */
106
   public function __construct()
107
   {
108
      /**
109
       * register PHPFSPOT class global
110
       *
111
       * @global PHPFSPOT $GLOBALS['phpfspot']
112
       * @name $phpfspot
113
       */
114
      $GLOBALS['phpfspot'] =& $this;
115
116
      $this->cfg = new PHPFSPOT_CFG;
117
118
      /* verify config settings */
119
      if($this->check_config_options()) {
120
         exit(1);
121
      }
122
123
      /* set application name and version information */
124
      $this->cfg->product = "phpfspot";
125
      $this->cfg->version = "1.7";
126
      $this->cfg->db_version = 2;
127
128
      $this->sort_orders= array(
129
         'date_asc' => 'Date &uarr;',
130
         'date_desc' => 'Date &darr;',
131
         'name_asc' => 'Name &uarr;',
132
         'name_desc' => 'Name &darr;',
133
         'tags_asc' => 'Tags &uarr;',
134
         'tags_desc' => 'Tags &darr;',
135
      );
136
137
      /* Check necessary requirements */
138
      if(!$this->check_requirements()) {
139
         exit(1);
140
      }
141
142
      /******* Opening F-Spot's sqlite database *********/
143
144
      /* Check if database file exists and is readable */
145
      if(!file_exists($this->cfg->fspot_db) || !is_readable($this->cfg->fspot_db)) {
146
         print "Error: ". $this->cfg->fspot_db ." does not exist or is not readable for user ". $this->getuid() .".\n";
147
         exit(1);
148
      }
149
150
      /* Check if database file is writeable */
151
      if(!is_writeable($this->cfg->fspot_db)) {
152
         print "Error: ". $this->cfg->fspot_db ." is not writeable for user ". $this->getuid() .".\n";
153
         print "Please fix permissions so phpfspot can create indices within the F-Spot database to"
154
            ." speed up some database operations.\n";
155
         exit(1);
156
      }
157
158
      /* open the database */
159
      $this->db  = new PHPFSPOT_DB($this, $this->cfg->fspot_db);
160
161
      /* change sqlite temp directory, if requested */
162
      if(isset($this->cfg->sqlite_temp_dir)) {
163
         $this->db->db_exec("
164
            PRAGMA
165
               temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
166
         ");
167
      }
168
169
      /* get F-Spot database version */
170
      $this->dbver = $this->getFspotDBVersion();
171
172
      if(!is_writeable($this->cfg->base_path ."/templates_c")) {
173
         print $this->cfg->base_path ."/templates_c: directory is not writeable for user ". $this->getuid() ."\n";
174
         exit(1);
175
      }
176
177
      if(!is_writeable($this->cfg->thumb_path)) {
178
         print $this->cfg->thumb_path .": directory is not writeable for user ". $this->getuid() ."\n";
179
         exit(1);
180
      }
181
182
      /******* Opening phpfspot's sqlite database *********/
183
184
      /* Check if directory where the database file is stored is writeable  */
185
      if(!is_writeable(dirname($this->cfg->phpfspot_db))) {
186
         print "Error: ". dirname($this->cfg->phpfspot_db) .": directory is not writeable for user ". $this->getuid() .".\n";
187
         print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n";
188
         exit(1);
189
      }
190
191
      /* Check if database file is writeable */
192
      if(file_exists($this->cfg->phpfspot_db) && !is_writeable($this->cfg->phpfspot_db)) {
193
         print "Error: ". $this->cfg->phpfspot_db ." is not writeable for user ". $this->getuid() .".\n";
194
         print "Please fix permissions so phpfspot can create its own sqlite database to store some settings.\n";
195
         exit(1);
196
      }
197
198
      /* open the database */
199
      $this->cfg_db = new PHPFSPOT_DB($this, $this->cfg->phpfspot_db);
200
201
      /* change sqlite temp directory, if requested */
202
      if(isset($this->cfg->sqlite_temp_dir)) {
203
         $this->cfg_db->db_exec("
204
            PRAGMA
205
               temp_store_directory = '". $this->cfg->sqlite_temp_dir ."'
206
         ");
207
      }
208
209
      /* Check if some tables need to be created */
210
      $this->check_phpfspot_db();
211
212
      /* overload Smarty class with our own template handler */
213
      require_once "phpfspot_tmpl.php";
214
      $this->tmpl = new PHPFSPOT_TMPL();
215
216
      /* pre-set some template variables */
217
      $this->tmpl->assign('web_path', $this->cfg->web_path);
218
219
      /* Starting with F-Spot 0.4.2, the rating-feature was available */
220
      if($this->dbver > 10) {
221
         $this->tmpl->assign('has_rating', true);
222
         $this->sort_orders = array_merge($this->sort_orders, array(
223
            'rate_asc' => 'Rate &uarr;',
224
            'rate_desc' => 'Rate &darr;',
225
         ));
226
      }
227
228
      /* check if all necessary indices exist */
229
      $this->checkDbIndices();
230
231
      /* if session is not yet started, do it now */
232
      if(session_id() == "")
233
         session_start();
234
235
      if(!isset($_SESSION['tag_condition']))
236
         $_SESSION['tag_condition'] = 'or';
237
238
      /* if sort-order has not been set yet, get the one specified in the config */
239
      if(!isset($_SESSION['sort_order']))
240
         $_SESSION['sort_order'] = $this->cfg->sort_order;
241
242
      if(!isset($_SESSION['searchfor_tag']))
243
         $_SESSION['searchfor_tag'] = '';
244
245
      // if begin_with is still set but thumbs_per_page is now 0, unset it
246
      if(isset($_SESSION['begin_with']) && $this->cfg->thumbs_per_page == 0)
247
         unset($_SESSION['begin_with']);
248
249
      // if user-friendly-url's are enabled, set also a flag for the template handler
250
      if($this->is_user_friendly_url()) {
251
         $this->tmpl->assign('user_friendly_url', 'true');
252
      }
253
254
   } // __construct()
255
256
   public function __destruct()
257
   {
258
259
   } // __destruct()
260
261
   /**
262
    * show - generate html output
263
    *
264
    * this function can be called after the constructor has
265
    * prepared everyhing. it will load the index.tpl smarty
266
    * template. if necessary it will registere pre-selects
267
    * (photo index, photo, tag search, date search) into
268
    * users session.
269
    */
270
   public function show()
271
   {
272
      $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
273
      $this->tmpl->assign('page_title', $this->cfg->page_title);
274
      $this->tmpl->assign('current_condition', $_SESSION['tag_condition']);
275
      $this->tmpl->assign('template_path', 'themes/'. $this->cfg->theme_name);
276
277
      /* parse URL */
278
      if($this->is_user_friendly_url()) {
279
         $content = $this->parse_user_friendly_url($_SERVER['REQUEST_URI']);
280
      }
281
282
      if(isset($_GET['mode'])) {
283
284
         $_SESSION['start_action'] = $_GET['mode'];
285
286
         switch($_GET['mode']) {
287
            case 'showpi':
288
               if(isset($_GET['tags'])) {
289
                  $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
290
               }
291
               if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
292
                  $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
293
               }
294
               if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
295
                  $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
296
               }
297
               break;
298
            case 'showp':
299
               if(isset($_GET['tags'])) {
300
                  $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
301
                  $_SESSION['start_action'] = 'showp';
302
               }
303
               if(isset($_GET['id']) && is_numeric($_GET['id'])) {
304
                  if($_SESSION['current_photo'] != $_GET['id'])
305
                     unset($_SESSION['current_version']);
306
                  $_SESSION['current_photo'] = $_GET['id'];
307
                  $_SESSION['start_action'] = 'showp';
308
               }
309
               if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
310
                  $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
311
               }
312
               if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
313
                  $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
314
               }
315
               break;
316
            case 'export':
317
               /* fetch export template */
318
               print $this->tmpl->fetch("export.tpl");
319
               /* no further execution necessary. */
320
               return;
321
               break;
322
            case 'slideshow':
323
               /* fetch slideshow template */
324
               print $this->tmpl->show("slideshow.tpl");
325
               /* no further execution necessary. */
326
               return;
327
               break;
328
            case 'rss':
329
               if(isset($_GET['tags'])) {
330
                  $_SESSION['selected_tags'] = $this->extractTags($_GET['tags']);
331
               }
332
               if(isset($_GET['from_date']) && $this->isValidDate($_GET['from_date'])) {
333
                  $_SESSION['from_date'] = strtotime($_GET['from_date'] ." 00:00:00");
334
               }
335
               if(isset($_GET['to_date']) && $this->isValidDate($_GET['to_date'])) {
336
                  $_SESSION['to_date'] = strtotime($_GET['to_date'] ." 23:59:59");
337
               }
338
               $this->getRSSFeed();
339
               return;
340
               break;
341
         }
342
      }
343
344
      /* if date-search variables are registered in the session, set the check
345
         for "consider date-range" in the html output
346
      */
347
      if(isset($_SESSION['from_date']) && isset($_SESSION['to_date']))
348
         $this->tmpl->assign('date_search_enabled', true);
349
350
      /* if rate-search variables are registered in the session, set the check
351
         for "consider rate-range" in the html output
352
      */
353
      if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
354
         $this->tmpl->assign('rate_search_enabled', true);
355
      }
356
357
      $this->tmpl->register_function("sort_select_list", array(&$this, "smarty_sort_select_list"), false);
358
      $this->tmpl->assign('search_from_date', $this->get_date_text_field('from'));
359
      $this->tmpl->assign('search_to_date', $this->get_date_text_field('to'));
360
361
      $this->tmpl->assign('preset_selected_tags', $this->getSelectedTags());
362
      $this->tmpl->assign('preset_available_tags', $this->getAvailableTags());
363
      $this->tmpl->assign('rate_search', $this->get_rate_search());
364
365
      /* if no site-content has been set yet... */
366
      if(!isset($content)) {
367
         /* if tags are already selected, we can immediately display photo-index */
368
         if((isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags']) &&
369
             isset($_SESSION['start_action']) && $_SESSION['start_action'] != 'showp') ||
370
            (isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi'))
371
            $this->tmpl->assign('initial_content', $this->showPhotoIndex());
372
         else {
373
            /* if a photo is already selected, we can immediately display single-photo */
374
            if(isset($_SESSION['current_photo']) && !empty($_SESSION['current_photo']))
375
               $this->tmpl->assign('initial_content', $this->showPhoto($_SESSION['current_photo']));
376
            else {
377
               /* ok, then let us show the welcome page... */
378
               $this->tmpl->assign('initial_content', $this->tmpl->fetch('welcome.tpl'));
379
            }
380
         }
381
      }
382
      else
383
         $this->tmpl->assign('initial_content', $content);
384
385
      $this->tmpl->show("index.tpl");
386
387
   } // show()
388
389
   /**
390
    * get_tags - grab all tags of f-spot's database
391
    *
392
    * this function will get all available tags from
393
    * the f-spot database and store them within two
394
    * arrays within this class for later usage. in
395
    * fact, if the user requests (hide_tags) it will
396
    * opt-out some of them.
397
    *
398
    * this function is getting called once by show()
399
    */
400
   private function get_tags()
401
   {
402
      $this->avail_tags = Array();
403
      $count = 0;
404
   
405
      /* if show_tags has been set in the configuration (only show photos
406
         which are tagged by these tags) they following will take care,
407
         that only these other tags are displayed where the photo is also
408
         tagged with one of show_tags.
409
      */
410
      if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
411
         $query_str="
412
            SELECT
413
               DISTINCT t1.id as id, t1.name as name
414
            FROM
415
               photo_tags pt1
416
            INNER JOIN photo_tags
417
               pt2 ON pt1.photo_id=pt2.photo_id
418
            INNER JOIN tags t1
419
               ON t1.id=pt1.tag_id
420
            INNER JOIN tags t2
421
               ON t2.id=pt2.tag_id
422
            WHERE
423
               t2.name IN  ('".implode("','",$this->cfg->show_tags)."')
424
            ORDER BY
425
               t1.sort_priority ASC";
426
427
         $result = $this->db->db_query($query_str);
428
      }
429
      else
430
      {
431
         $result = $this->db->db_query("
432
            SELECT id as id,name as name
433
            FROM tags
434
            ORDER BY sort_priority ASC
435
         ");
436
      }
437
      
438
      while($row = $this->db->db_fetch_object($result)) {
439
440
         $tag_id = $row['id'];
441
         $tag_name = $row['name'];
442
443
         /* if the user has specified to ignore this tag in phpfspot's
444
            configuration, ignore it here so it does not get added to
445
            the tag list.
446
         */
447
         if(in_array($row['name'], $this->cfg->hide_tags))
448
            continue;
449
450
         /* if you include the following if-clause and the user has specified
451
            to only show certain tags which are specified in phpfspot's
452
            configuration, ignore all others so they will not be added to the
453
            tag list.
454
         if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags) &&
455
            !in_array($row['name'], $this->cfg->show_tags))
456
            continue;
457
         */
458
459
         $this->tags[$tag_id] = $tag_name; 
460
         $this->avail_tags[$count] = $tag_id;
461
         $count++;
462
463
      }
464
465
   } // get_tags()
466
467
   /** 
468
    * get all photo details from F-Spot database
469
    * 
470
    * this function queries the F-Spot database for all available
471
    * details of the requested photo. It returns them as a object.
472
    *
473
    * Furthermore it takes care of the photo version to be requested.
474
    * If photo version is not yet, it queries information for the
475
    * original photo.
476
    *
477
    * @param integer $idx
478
    * @return object|null
479
    */
480
   public function get_photo_details($idx, $version_idx = NULL)
481
   {
482
      /* ~ F-Spot version 0.3.x */
483
      if($this->dbver < 9) {
484
         $query_str = "
485
            SELECT
486
               p.id as id,
487
               p.name as name,
488
               p.time as time,
489
               p.directory_path as directory_path,
490
               p.description as description
491
            FROM
492
               photos p
493
         ";
494
      }
495
      else {
496
         /* till F-Spot version 0.4.1 */
497
         if($this->dbver < 11) {
498
            $query_str = "
499
               SELECT
500
                  p.id as id,
501
                  p.uri as uri,
502
                  p.time as time,
503
                  p.description as description
504
               FROM
505
                  photos p
506
            ";
507
         }
508
         elseif($this->dbver < 17) {
509
             /* rating value got introduced */
510
            $query_str = "
511
               SELECT
512
                  p.id as id,
513
                  p.uri as uri,
514
                  p.time as time,
515
                  p.description as description,
516
                  p.rating as rating
517
               FROM
518
                  photos p
519
            ";
520
         }
521
         else {
522
            /* path & filename now splited in base_uri & filename */
523
            $query_str = "
524
               SELECT
525
                  p.id as id,
526
                  p.base_uri || p.filename as uri,
527
                  p.time as time,
528
                  p.description as description,
529
                  p.rating as rating
530
               FROM
531
                  photos p
532
            ";
533
         }
534
      }
535
536
      /* if show_tags is set, only return details of photos which are
537
         tagged with a tag that has been specified to be shown.
538
      */
539
      if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
540
         $query_str.= "
541
            INNER JOIN photo_tags pt
542
               ON p.id=pt.photo_id
543
            INNER JOIN tags t
544
               ON pt.tag_id=t.id
545
            WHERE p.id='". $idx ."'
546
            AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
547
      }
548
      else {
549
         $query_str.= "
550
            WHERE p.id='". $idx ."'
551
         ";
552
      }
553
554
      if(!$row = $this->db->db_fetchSingleRow($query_str))
555
         return null;
556
557
      /* before F-Spot db version 9 there was no uri column but
558
         seperated fields for directory_path and name (= filename).
559
      */
560
      if($this->dbver < 9) {
561
         $row['uri'] = "file://". $row['directory_path'] ."/". $row['name'];
562
      }
563
      /* starting with dbversion >= 17 we need to rawurldecode() uri */
564
      elseif($this->dbver >= 17) {
565
         $row['uri'] = rawurldecode($row['uri']);
566
      }
567
568
      /* if version-idx has not yet been set, get the latest photo version */
569
      if(!isset($version_idx) || !$this->is_valid_version($idx, $version_idx))
570
         $version_idx = $this->get_latest_version($idx);
571
572
      /* if an alternative version has been requested. But we
573
         support this only for F-Spot database versions from
574
         v9.
575
      */
576
      if($version_idx > 0 && $this->dbver >= 9) {
577
         if ($this->dbver < 17) {
578
            /* check for alternative versions */
579
            if($version = $this->db->db_fetchSingleRow("
580
               SELECT
581
                  version_id, name, uri
582
               FROM
583
                  photo_versions
584
               WHERE
585
                  photo_id LIKE '". $idx ."'
586
               AND
587
                  version_id LIKE '". $version_idx ."'")) {
588
589
               $row['name'] = $version['name'];
590
               $row['uri'] = $version['uri'];
591
            }
592
         }
593
         else {
594
            /* path & filename now splited in base_uri & filename */
595
            if($version = $this->db->db_fetchSingleRow("
596
               SELECT
597
                  version_id,
598
                  name,
599
                  base_uri || filename as uri
600
               FROM
601
                  photo_versions
602
               WHERE
603
                  photo_id LIKE '". $idx ."'
604
               AND
605
                  version_id LIKE '". $version_idx ."'")) {
606
607
               $row['name'] = $version['name'];
608
               $row['uri'] = rawurldecode($version['uri']);
609
            }
610
         }
611
      }
612
613
      return $row;
614
615
   } // get_photo_details()
616
617
   /**
618
    * returns aligned photo names 
619
    *
620
    * this function returns aligned (length) names for a specific photo.
621
    * If the length of the name exceeds $limit the name will bei
622
    * shrinked (...).
623
    *
624
    * @param integer $idx
625
    * @param integer $limit
626
    * @return string|null
627
    */
628
   public function getPhotoName($idx, $limit = 0)
629
   {
630
      if($details = $this->get_photo_details($idx)) {
631
         if($long_name = $this->parse_uri($details['uri'], 'filename')) {
632
            $name = $this->shrink_text($long_name, $limit);
633
            return $name;
634
         }
635
      }
636
637
      return null;
638
639
   } // getPhotoName()
640
641
   /**
642
    * get photo rating level
643
    *
644
    * this function will return the integer-based rating level of a
645
    * photo. This can only be done, if the F-Spot database is at a
646
    * specific version. If rating value can not be found, zero will
647
    * be returned indicating no rating value is available.
648
    *
649
    * @param integer idx
650
    * @return integer
651
    */
652
   public function get_photo_rating($idx)
653
   {
654
      if($detail = $this->get_photo_details($idx)) {
655
         if(isset($detail['rating']))
656
            return $detail['rating'];
657
      }
658
659
      return 0;
660
661
   } // get_photo_rating()
662
663
   /**
664
    * get rate-search bars
665
    *
666
    * this function will return the rating-bars for the search field.
667
    *
668
    * @return string
669
    */
670
   public function get_rate_search()
671
   {
672
      $bar = "";
673
674
      for($i = 1; $i <= 5; $i++) {
675
676
         $bar.= "<img id=\"rate_from_". $i ."\" src=\"";
677
678
         if(isset($_SESSION['rate_from']) && $i <= $_SESSION['rate_from'])
679
            $bar.= $this->cfg->web_path ."/resources/star.png";
680
         else
681
            $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
682
683
         $bar.= "\"
684
            onmouseover=\"show_rate('from', ". $i .");\"
685
            onmouseout=\"reset_rate('from');\"
686
            onclick=\"set_rate('from', ". $i .")\" />";
687
      }
688
689
      $bar.= "<br />\n";
690
691
       for($i = 1; $i <= 5; $i++) {
692
693
         $bar.= "<img id=\"rate_to_". $i ."\" src=\"";
694
695
         if(isset($_SESSION['rate_to']) && $i <= $_SESSION['rate_to'])
696
            $bar.= $this->cfg->web_path ."/resources/star.png";
697
         else
698
            $bar.= $this->cfg->web_path ."/resources/empty_rate.png";
699
700
         $bar.= "\"
701
            onmouseover=\"show_rate('to', ". $i .");\"
702
            onmouseout=\"reset_rate('to');\"
703
            onclick=\"set_rate('to', ". $i .");\" />";
704
      }
705
706
      return $bar;
707
708
   } // get_rate_search()
709
710
   /**
711
    * shrink text according provided limit
712
    *
713
    * If the length of the name exceeds $limit, text will be shortend
714
    * and inner content will be replaced with "...".
715
    *
716
    * @param string $ext
717
    * @param integer $limit
718
    * @return string
719
    */
720
   private function shrink_text($text, $limit)
721
   {
722
      if($limit != 0 && strlen($text) > $limit) {
723
         $text = substr($text, 0, $limit-5) ."...". substr($text, -($limit-5));
724
      }
725
726
      return $text;
727
728
   } // shrink_text();
729
730
   /**
731
    * translate f-spoth photo path
732
    * 
733
    * as the full-qualified path recorded in the f-spot database
734
    * is usally not the same as on the webserver, this function
735
    * will replace the path with that one specified in the cfg
736
    * @param string $path
737
    * @return string
738
    */
739
   public function translate_path($path)
740
   {
741
      if($this->cfg->enable_replace_path == true)
742
         return str_replace(
743
            $this->cfg->path_replace_from,
744
            $this->cfg->path_replace_to, $path);
745
746
      return $path;
747
748
   } // translate_path
749
750
   /**
751
    * control HTML ouput for a single photo
752
    *
753
    * this function provides all the necessary information
754
    * for the single photo template.
755
    * @param integer photo
756
    */
757
   public function showPhoto($photo)
758
   {
759
      /* get all photos from the current photo selection */
760
      $all_photos = $this->getPhotoSelection();
761
      $count = count($all_photos);
762
763
      for($i = 0; $i < $count; $i++) {
764
         
765
         // $get_next will be set, when the photo which has to
766
         // be displayed has been found - this means that the
767
         // next available is in fact the NEXT image (for the
768
         // navigation icons) 
769
         if(isset($get_next)) {
770
            $next_img = $all_photos[$i];
771
            break;
772
         }
773
774
         /* the next photo is our NEXT photo */
775
         if($all_photos[$i] == $photo) {
776
            $get_next = 1;
777
         }
778
         else {
779
            $previous_img = $all_photos[$i];
780
         }
781
782
         if($photo == $all_photos[$i]) {
783
               $current = $i;
784
         }
785
      }
786
787
      $details = $this->get_photo_details($photo);
788
789
      if(!$details) {
790
         print "error";
791
         return;
792
      }
793
794
      $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
795
796
      /* if current version is already set, use it */
797
      if($this->get_current_version() !== false)
798
         $version = $this->get_current_version();
799
800
      /* if version not set yet, we assume to display the latest version */
801
      if(!isset($version) || !$this->is_valid_version($photo, $version))
802
         $version = $this->get_latest_version($photo);
803
804
      $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo, $version);
805
806
      if(!file_exists($orig_path)) {
807
         $this->_error("Photo ". $orig_path ." does not exist!<br />\n");
808
         return;
809
      }
810
811
      if(!is_readable($orig_path)) {
812
         $this->_error("Photo ". $orig_path ." is not readable for user ". $this->getuid() ."<br />\n");
813
         return;
814
      }
815
816
      /* If the thumbnail doesn't exist yet, try to create it */
817
      if(!file_exists($thumb_path)) {
818
         $this->gen_thumb($photo, true);
819
         $thumb_path = $this->get_thumb_path($this->cfg->photo_width, $photo, $version);
820
      }
821
822
      /* get mime-type, height and width from the original photo */
823
      $info = getimagesize($orig_path);
824
825
      /* get EXIF information if JPEG */
826
      if(isset($info['mime']) && $info['mime'] == "image/jpeg") {
827
         $meta = $this->get_meta_informations($orig_path);
828
      }
829
830
      /* If EXIF data are available, use them */
831
      if(isset($meta['ExifImageWidth'])) {
832
         $meta_res = $meta['ExifImageWidth'] ."x". $meta['ExifImageLength'];
833
      } else {
834
         $meta_res = $info[0] ."x". $info[1]; 
835
      }
836
837
      $meta_make = isset($meta['Make']) ? $meta['Make'] ." / ". $meta['Model'] : "n/a";
838
      $meta_size = isset($meta['FileSize']) ? round($meta['FileSize']/1024, 1) ."kbyte" : "n/a";
839
840
      $extern_link = "index.php?mode=showp&id=". $photo;
841
      $current_tags = $this->getCurrentTags();
842
      if($current_tags != "") {
843
         $extern_link.= "&tags=". $current_tags;
844
      }
845
      if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
846
         $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
847
      }
848
849
      $this->tmpl->assign('extern_link', $extern_link);
850
851
      if(!file_exists($thumb_path)) {
852
         $this->_error("Can't open file ". $thumb_path ."\n");
853
         return;
854
      }
855
856
      $info_thumb = getimagesize($thumb_path);
857
858
      $this->tmpl->assign('description', $details['description']);
859
      $this->tmpl->assign('image_name', $this->parse_uri($details['uri'], 'filename'));
860
      $this->tmpl->assign('image_rating', $this->get_photo_rating($photo));
861
862
      $this->tmpl->assign('width', $info_thumb[0]);
863
      $this->tmpl->assign('height', $info_thumb[1]);
864
      $this->tmpl->assign('ExifMadeOn', strftime("%a %x %X", $details['time']));
865
      $this->tmpl->assign('ExifMadeWith', $meta_make);
866
      $this->tmpl->assign('ExifOrigResolution', $meta_res);
867
      $this->tmpl->assign('ExifFileSize', $meta_size);
868
 
869
      if($this->is_user_friendly_url()) {
870
         $this->tmpl->assign('image_url', '/photo/'. $photo ."/". $this->cfg->photo_width .'/'. $version);
871
         $this->tmpl->assign('image_url_full', '/photo/'. $photo);
872
      }
873
      else {
874
         $this->tmpl->assign('image_url', 'phpfspot_img.php?idx='. $photo ."&amp;width=". $this->cfg->photo_width ."&amp;version=". $version);
875
         $this->tmpl->assign('image_url_full', 'phpfspot_img.php?idx='. $photo);
876
      }
877
878
      $this->tmpl->assign('image_filename', $this->parse_uri($details['uri'], 'filename'));
879
880
      $this->tmpl->assign('tags', $this->get_photo_tags($photo));
881
      $this->tmpl->assign('current_page', $this->getCurrentPage($current, $count));
882
      $this->tmpl->assign('current_img', $photo);
883
884
      if(isset($previous_img)) {
885
         $this->tmpl->assign('previous_url', "javascript:showPhoto(". $previous_img .");");
886
         $this->tmpl->assign('prev_img', $previous_img);
887
      }
888
889
      if(isset($next_img)) {
890
         $this->tmpl->assign('next_url', "javascript:showPhoto(". $next_img .");");
891
         $this->tmpl->assign('next_img', $next_img);
892
      }
893
894
      $this->tmpl->assign('mini_width', $this->cfg->mini_width);
895
      $this->tmpl->assign('photo_width', $this->cfg->photo_width);
896
      $this->tmpl->assign('photo_number', $i);
897
      $this->tmpl->assign('photo_count', count($all_photos));
898
      $this->tmpl->assign('photo', $photo);
899
      $this->tmpl->assign('version', $version);
900
901
      /* if the photo as alternative versions, set a flag for the template */
902
      if($this->get_photo_versions($photo))
903
         $this->tmpl->assign('has_versions', true);
904
905
      $this->tmpl->register_function("photo_version_select_list", array(&$this, "smarty_photo_version_select_list"), false);
906
907
      return $this->tmpl->fetch("single_photo.tpl");
908
909
   } // showPhoto()
910
911
   /**
912
    * all available tags and tag cloud
913
    *
914
    * this function outputs all available tags (time ordered)
915
    * and in addition output them as tag cloud (tags which have
916
    * many photos will appears more then others)
917
    */
918
   public function getAvailableTags()
919
   {
920
      /* retrive tags from database */
921
      $this->get_tags();
922
923
      $output = "";
924
925
      $result = $this->db->db_query("
926
         SELECT tag_id as id, count(tag_id) as quantity
927
         FROM photo_tags
928
         INNER JOIN tags t
929
            ON t.id = tag_id
930
         GROUP BY tag_id
931
         ORDER BY t.name ASC
932
      ");
933
934
      $tags = Array();
935
936
      while($row = $this->db->db_fetch_object($result)) {
937
         $tags[$row['id']] = $row['quantity'];
938
      }
939
940
      // change these font sizes if you will
941
      $max_size = 125; // max font size in %
942
      $min_size = 75; // min font size in %
943
944
      // color
945
      $max_sat = hexdec('cc');
946
      $min_sat = hexdec('44');
947
948
      // get the largest and smallest array values
949
      $max_qty = max(array_values($tags));
950
      $min_qty = min(array_values($tags));
951
952
      // find the range of values
953
      $spread = $max_qty - $min_qty;
954
      if (0 == $spread) { // we don't want to divide by zero
955
         $spread = 1;
956
      }
957
958
      // determine the font-size increment
959
      // this is the increase per tag quantity (times used)
960
      $step = ($max_size - $min_size)/($spread);
961
      $step_sat = ($max_sat - $min_sat)/($spread);
962
963
      // loop through our tag array
964
      foreach ($tags as $key => $value) {
965
966
         /* has the currently processed tag already been added to
967
            the selected tag list? if so, ignore it here...
968
         */
969
         if(isset($_SESSION['selected_tags']) && in_array($key, $_SESSION['selected_tags']))
970
            continue;
971
972
         // calculate CSS font-size
973
         // find the $value in excess of $min_qty
974
         // multiply by the font-size increment ($size)
975
         // and add the $min_size set above
976
         $size = $min_size + (($value - $min_qty) * $step);
977
         // uncomment if you want sizes in whole %:
978
         $size = ceil($size);
979
980
         $color = $min_sat + ($value - $min_qty) * $step_sat;
981
982
         $r = '44';
983
         $g = dechex($color);
984
         $b = '88';
985
986
         if(isset($this->tags[$key])) {
987
            if($this->is_user_friendly_url()) {
988
               $output.= "<a href=\"". $this->cfg->web_path ."/tag/". $key ."\"
989
                  onclick=\"Tags('add', ". $key ."); return false;\"
990
                  class=\"tag\"
991
                  style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
992
                  title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
993
            }
994
            else {
995
               $output.= "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi\"
996
                  onclick=\"Tags('add', ". $key ."); return false;\"
997
                  class=\"tag\"
998
                  style=\"font-size: ". $size ."%; color: #". $r.$g.$b .";\"
999
                  title=\"Tag ". $this->tags[$key] .", ". $tags[$key] ." picture(s)\">". $this->tags[$key] ."</a>, ";
1000
            }
1001
         }
1002
      }
1003
1004
      $output = substr($output, 0, strlen($output)-2);
1005
      return $output;
1006
1007
   } // getAvailableTags()
1008
1009
   /**
1010
    * output all selected tags
1011
    *
1012
    * this function output all tags which have been selected
1013
    * by the user. the selected tags are stored in the 
1014
    * session-variable $_SESSION['selected_tags']
1015
    * @return string
1016
    */
1017
   public function getSelectedTags($type = 'link')
1018
   {
1019
      /* retrive tags from database */
1020
      $this->get_tags();
1021
1022
      $output = "";
1023
1024
      foreach($this->avail_tags as $tag)
1025
      {
1026
         // return all selected tags
1027
         if(isset($_SESSION['selected_tags']) && in_array($tag, $_SESSION['selected_tags'])) {
1028
1029
            switch($type) {
1030
               default:
1031
               case 'link':
1032
                  $output.= "<a href=\"javascript:Tags('del', ". $tag .");\" class=\"tag\">". $this->tags[$tag] ."</a>, ";
1033
                  break;
1034
               case 'img':
1035
                  $output.= "
1036
                  <div class=\"tagresulttag\">
1037
                   <a href=\"javascript:Tags('del', ". $tag .");\" title=\"". $this->tags[$tag] ."\">
1038
                    <img src=\"". $this->cfg->web_path ."/phpfspot_img.php?tagidx=". $tag ."\" />
1039
                   </a>
1040
                  </div>
1041
                  ";
1042
                  break;
1043
            }
1044
         }
1045
      }
1046
1047
      if($output != "") {
1048
         $output = substr($output, 0, strlen($output)-2);
1049
         return $output;
1050
      }
1051
      else {
1052
         return "no tags selected";
1053
      }
1054
1055
   } // getSelectedTags()
1056
1057
   /**
1058
    * add tag to users session variable
1059
    *
1060
    * this function will add the specified to users current
1061
    * tag selection. if a date search has been made before
1062
    * it will be now cleared
1063
    * @return string
1064
    */
1065
   public function addTag($tag)
1066
   {
1067
      if(!isset($_SESSION['selected_tags']))
1068
         $_SESSION['selected_tags'] = Array();
1069
1070
      if(isset($_SESSION['searchfor_tag']))
1071
         unset($_SESSION['searchfor_tag']);
1072
1073
      // has the user requested to hide this tag, and still someone,
1074
      // somehow tries to add it, don't allow this.
1075
      if(!isset($this->cfg->hide_tags) &&
1076
         in_array($this->get_tag_name($tag), $this->cfg->hide_tags))
1077
         return "ok";
1078
1079
      if(!in_array($tag, $_SESSION['selected_tags']))
1080
         array_push($_SESSION['selected_tags'], $tag);
1081
1082
      return "ok";
1083
   
1084
   } // addTag()
1085
1086
   /**
1087
    * remove tag to users session variable
1088
    *
1089
    * this function removes the specified tag from
1090
    * users current tag selection
1091
    * @param string $tag
1092
    * @return string
1093
    */
1094
   public function delTag($tag)
1095
   {
1096
      if(isset($_SESSION['searchfor_tag']))
1097
         unset($_SESSION['searchfor_tag']);
1098
1099
      if(isset($_SESSION['selected_tags'])) {
1100
         $key = array_search($tag, $_SESSION['selected_tags']);
1101
         unset($_SESSION['selected_tags'][$key]);
1102
         sort($_SESSION['selected_tags']);
1103
      }
1104
1105
      return "ok";
1106
1107
   } // delTag()
1108
1109
   /**
1110
    * reset tag selection
1111
    *
1112
    * if there is any tag selection, it will be
1113
    * deleted now
1114
    */
1115
   public function resetTags()
1116
   {
1117
      if(isset($_SESSION['selected_tags']))
1118
         unset($_SESSION['selected_tags']);
1119
1120
   } // resetTags()
1121
1122
   /**
1123
    * returns the value for the autocomplete tag-search
1124
    * @return string
1125
    */
1126
   public function get_xml_tag_list()
1127
   {
1128
      if(!isset($_GET['search']) || !is_string($_GET['search']))
1129
         $_GET['search'] = '';
1130
      
1131
      $length = 15;
1132
      $i = 1;
1133
         
1134
      /* retrive tags from database */
1135
      $this->get_tags();
1136
1137
      $matched_tags = Array();
1138
1139
      header("Content-Type: text/xml");
1140
1141
      $string = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n";
1142
      $string.= "<results>\n";
1143
1144
      foreach($this->avail_tags as $tag)
1145
      {
1146
         if(!empty($_GET['search']) &&
1147
            preg_match("/". $_GET['search'] ."/i", $this->tags[$tag]) &&
1148
            count($matched_tags) < $length) {
1149
1150
            $count = $this->get_num_photos($tag);
1151
1152
            if($count == 1) {
1153
               $string.= " <rs id=\"". $i ."\" info=\"". $count ." photo\">". $this->tags[$tag] ."</rs>\n";
1154
            }
1155
            else {
1156
               $string.= " <rs id=\"". $i ."\" info=\"". $count ." photos\">". $this->tags[$tag] ."</rs>\n";
1157
1158
            }
1159
            $i++;
1160
         }
1161
1162
         /* if we have collected enough items, break out */
1163
         if(count($matched_tags) >= $length)
1164
            break;
1165
      }
1166
1167
      $string.= "</results>\n";
1168
1169
      return $string;
1170
1171
   } // get_xml_tag_list()
1172
1173
1174
   /**
1175
    * reset single photo
1176
    *
1177
    * if a specific photo was requested (external link)
1178
    * unset the session variable now
1179
    */
1180
   public function resetPhotoView()
1181
   {
1182
      if(isset($_SESSION['current_photo']))
1183
         unset($_SESSION['current_photo']);
1184
1185
      if(isset($_SESSION['current_version']))
1186
         unset($_SESSION['current_version']);
1187
1188
   } // resetPhotoView();
1189
1190
   /**
1191
    * reset tag search
1192
    *
1193
    * if any tag search has taken place, reset it now
1194
    */
1195
   public function resetTagSearch()
1196
   {
1197
      if(isset($_SESSION['searchfor_tag']))
1198
         unset($_SESSION['searchfor_tag']);
1199
1200
   } // resetTagSearch()
1201
1202
   /**
1203
    * reset name search
1204
    *
1205
    * if any name search has taken place, reset it now
1206
    */
1207
   public function resetNameSearch()
1208
   {
1209
      if(isset($_SESSION['searchfor_name']))
1210
         unset($_SESSION['searchfor_name']);
1211
1212
   } // resetNameSearch()
1213
1214
   /**
1215
    * reset date search
1216
    *
1217
    * if any date search has taken place, reset it now.
1218
    */
1219
   public function resetDateSearch()
1220
   {
1221
      if(isset($_SESSION['from_date']))
1222
         unset($_SESSION['from_date']);
1223
      if(isset($_SESSION['to_date']))
1224
         unset($_SESSION['to_date']);
1225
1226
   } // resetDateSearch();
1227
1228
   /**
1229
    * reset rate search
1230
    *
1231
    * if any rate search has taken place, reset it now.
1232
    */
1233
   public function resetRateSearch()
1234
   {
1235
      if(isset($_SESSION['rate_from']))
1236
         unset($_SESSION['rate_from']);
1237
      if(isset($_SESSION['rate_to']))
1238
         unset($_SESSION['rate_to']);
1239
1240
   } // resetRateSearch();
1241
1242
   /**
1243
    * return all photo according selection
1244
    *
1245
    * this function returns all photos based on
1246
    * the tag-selection, tag- or date-search.
1247
    * the tag-search also has to take care of AND
1248
    * and OR conjunctions
1249
    * @return array
1250
    */
1251
   public function getPhotoSelection()
1252
   {
1253
      $matched_photos = Array();
1254
      $additional_where_cond = "";
1255
1256
      if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1257
         $from_date = $_SESSION['from_date'];
1258
         $to_date = $_SESSION['to_date'];
1259
         $additional_where_cond.= "
1260
               p.time>='". $from_date ."'
1261
            AND
1262
               p.time<='". $to_date ."'
1263
         ";
1264
      } 
1265
1266
      if(isset($_SESSION['searchfor_name'])) {
1267
1268
         /* check for previous conditions. if so add 'AND' */
1269
         if(!empty($additional_where_cond)) {
1270
            $additional_where_cond.= " AND ";
1271
         }
1272
1273
         if($this->dbver < 9) {
1274
            $additional_where_cond.= "
1275
                  (
1276
                        p.name LIKE '%". $_SESSION['searchfor_name'] ."%'
1277
                     OR
1278
                        p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1279
                  )
1280
            ";
1281
         }
1282
         if($this->dbver < 17) {
1283
            $additional_where_cond.= "
1284
                  (
1285
                        basename(p.uri) LIKE '%". $_SESSION['searchfor_name'] ."%'
1286
                     OR
1287
                        p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1288
                  )
1289
            ";
1290
         }
1291
         else {
1292
            $additional_where_cond.= "
1293
                  (
1294
                        p.filename LIKE '%". $_SESSION['searchfor_name'] ."%'
1295
                     OR
1296
                        p.description LIKE '%". $_SESSION['searchfor_name'] ."%'
1297
                  )
1298
            ";
1299
         }
1300
      }
1301
1302
      /* limit result based on rate-search */
1303
      if(isset($_SESSION['rate_from']) && isset($_SESSION['rate_to'])) {
1304
1305
         if($this->dbver > 10) {
1306
1307
            /* check for previous conditions. if so add 'AND' */
1308
            if(!empty($additional_where_cond)) {
1309
               $additional_where_cond.= " AND ";
1310
            }
1311
1312
            $additional_where_cond.= "
1313
                     p.rating >= ". $_SESSION['rate_from'] ."
1314
                  AND
1315
                     p.rating <= ". $_SESSION['rate_to'] ."
1316
            ";
1317
         }
1318
      }
1319
1320
      if(isset($_SESSION['sort_order'])) {
1321
         $order_str = $this->get_sort_order();
1322
      }
1323
1324
      /* return a search result */
1325
      if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '') {
1326
         $query_str = "
1327
            SELECT DISTINCT
1328
               pt1.photo_id as photo_id
1329
            FROM
1330
               photo_tags pt1
1331
            INNER JOIN photo_tags pt2
1332
               ON pt1.photo_id=pt2.photo_id
1333
            INNER JOIN tags t
1334
               ON pt1.tag_id=t.id
1335
            INNER JOIN photos p
1336
               ON pt1.photo_id=p.id
1337
            INNER JOIN tags t2
1338
               ON pt2.tag_id=t2.id
1339
            WHERE t.name LIKE '%". $_SESSION['searchfor_tag'] ."%' ";
1340
1341
         if(!empty($additional_where_cond))
1342
            $query_str.= "AND ". $additional_where_cond ." ";
1343
1344
         if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1345
            $query_str.= "AND t2.name IN ('".implode("','",$this->cfg->show_tags)."')";
1346
         }
1347
         
1348
         if(isset($order_str))
1349
            $query_str.= $order_str;
1350
1351
         $result = $this->db->db_query($query_str);
1352
         while($row = $this->db->db_fetch_object($result)) {
1353
            array_push($matched_photos, $row['photo_id']);
1354
         }
1355
         return $matched_photos;
1356
      }
1357
1358
      /* return according the selected tags */
1359
      if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1360
         $selected = "";
1361
         foreach($_SESSION['selected_tags'] as $tag)
1362
            $selected.= $tag .",";
1363
         $selected = substr($selected, 0, strlen($selected)-1);
1364
1365
         /* photo has to match at least on of the selected tags */
1366
         if($_SESSION['tag_condition'] == 'or') {
1367
            $query_str = "
1368
               SELECT DISTINCT
1369
                  pt1.photo_id as photo_id
1370
               FROM
1371
                  photo_tags pt1
1372
               INNER JOIN photo_tags pt2
1373
                  ON pt1.photo_id=pt2.photo_id
1374
               INNER JOIN tags t
1375
                  ON pt2.tag_id=t.id
1376
               INNER JOIN photos p
1377
                  ON pt1.photo_id=p.id
1378
               WHERE pt1.tag_id IN (". $selected .")
1379
            ";
1380
            if(!empty($additional_where_cond))
1381
               $query_str.= "AND ". $additional_where_cond ." ";
1382
1383
            if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1384
               $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags)."')";
1385
            }
1386
1387
            if(isset($order_str))
1388
               $query_str.= $order_str;
1389
         }
1390
         /* photo has to match all selected tags */
1391
         elseif($_SESSION['tag_condition'] == 'and') {
1392
1393
            if(count($_SESSION['selected_tags']) >= 32) {
1394
               print "A SQLite limit of 32 tables within a JOIN SELECT avoids to<br />\n";
1395
               print "evaluate your tag selection. Please remove some tags from your selection.\n";
1396
               return Array();
1397
            } 
1398
1399
            /* Join together a table looking like
1400
1401
               pt1.photo_id pt1.tag_id pt2.photo_id pt2.tag_id ...
1402
1403
               so the query can quickly return all images matching the
1404
               selected tags in an AND condition
1405
1406
            */
1407
1408
            $query_str = "
1409
               SELECT DISTINCT
1410
                  pt1.photo_id as photo_id
1411
               FROM
1412
                  photo_tags pt1
1413
            ";
1414
1415
            if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1416
               $query_str.= "
1417
                  INNER JOIN tags t
1418
                     ON pt1.tag_id=t.id
1419
               ";
1420
            }
1421
1422
            for($i = 0; $i < count($_SESSION['selected_tags']); $i++) {
1423
               $query_str.= "
1424
                  INNER JOIN photo_tags pt". ($i+2) ."
1425
                     ON pt1.photo_id=pt". ($i+2) .".photo_id
1426
               ";
1427
            }
1428
            $query_str.= "
1429
               INNER JOIN photos p
1430
                  ON pt1.photo_id=p.id
1431
            ";
1432
            $query_str.= "WHERE pt2.tag_id=". $_SESSION['selected_tags'][0]." ";
1433
            for($i = 1; $i < count($_SESSION['selected_tags']); $i++) {
1434
               $query_str.= "
1435
                  AND pt". ($i+2) .".tag_id=". $_SESSION['selected_tags'][$i] ."
1436
               "; 
1437
            }
1438
            if(!empty($additional_where_cond))
1439
               $query_str.= "AND ". $additional_where_cond;
1440
1441
            if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1442
               $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1443
            }
1444
1445
            if(isset($order_str))
1446
               $query_str.= $order_str;
1447
1448
         }
1449
1450
         $result = $this->db->db_query($query_str);
1451
         while($row = $this->db->db_fetch_object($result)) {
1452
            array_push($matched_photos, $row['photo_id']);
1453
         }
1454
         return $matched_photos;
1455
      }
1456
1457
      /* return all available photos */
1458
      $query_str = "
1459
         SELECT DISTINCT
1460
            p.id as id
1461
         FROM
1462
            photos p
1463
         LEFT JOIN photo_tags pt
1464
            ON p.id=pt.photo_id
1465
         LEFT JOIN tags t
1466
            ON pt.tag_id=t.id
1467
      ";
1468
1469
      if(!empty($additional_where_cond))
1470
         $query_str.= "WHERE ". $additional_where_cond ." ";
1471
1472
      if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
1473
         if(!empty($additional_where_cond))
1474
            $query_str.= "AND t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1475
         else
1476
            $query_str.= "WHERE t.name IN ('".implode("','",$this->cfg->show_tags). "')";
1477
      }
1478
 
1479
      if(isset($order_str))
1480
         $query_str.= $order_str;
1481
1482
      $result = $this->db->db_query($query_str);
1483
      while($row = $this->db->db_fetch_object($result)) {
1484
         array_push($matched_photos, $row['id']);
1485
      }
1486
      return $matched_photos;
1487
1488
   } // getPhotoSelection()
1489
1490
    /**
1491
    * control HTML ouput for photo index
1492
    *
1493
    * this function provides all the necessary information
1494
    * for the photo index template.
1495
    * @return string
1496
    */
1497
   public function showPhotoIndex()
1498
   {
1499
      $photos = $this->getPhotoSelection();
1500
      $current_tags = $this->getCurrentTags();
1501
1502
      $count = count($photos);
1503
1504
      /* if all thumbnails should be shown on one page */
1505
      if(!isset($this->cfg->thumbs_per_page) || $this->cfg->thumbs_per_page == 0) {
1506
         $begin_with = 0;
1507
         $end_with = $count;
1508
      }
1509
      /* thumbnails should be splitted up in several pages */
1510
      elseif($this->cfg->thumbs_per_page > 0) {
1511
1512
         if(!isset($_SESSION['begin_with']) || $_SESSION['begin_with'] == 0) {
1513
            $begin_with = 0;
1514
         }
1515
         else {
1516
            $begin_with = $_SESSION['begin_with'];
1517
         }
1518
1519
         $end_with = $begin_with + $this->cfg->thumbs_per_page;
1520
      }
1521
1522
      $thumbs = 0;
1523
1524
      for($i = $begin_with; $i < $end_with; $i++) {
1525
1526
         if(!isset($photos[$i]))
1527
            continue;
1528
1529
         /* on first run, initalize all used variables */
1530
         if($thumbs == 0) {
1531
            $images = Array();
1532
            $images[$thumbs]        = Array();
1533
            $img_height[$thumbs]    = Array();
1534
            $img_width[$thumbs]     = Array();
1535
            $img_id[$thumbs]        = Array();
1536
            $img_name[$thumbs]      = Array();
1537
            $img_fullname[$thumbs]  = Array();
1538
            $img_title              = Array();
1539
            $img_rating             = Array();
1540
         }
1541
1542
         $images[$thumbs] = $photos[$i];
1543
         $img_id[$thumbs] = $i;
1544
         $img_name[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 15));
1545
         $img_fullname[$thumbs] = htmlspecialchars($this->getPhotoName($photos[$i], 0));
1546
         $img_title[$thumbs] = "Click to view photo ". htmlspecialchars($this->getPhotoName($photos[$i], 0));
1547
         $img_rating[$thumbs] = $this->get_photo_rating($photos[$i]);
1548
1549
         /* get local path of the thumbnail image to be displayed */
1550
         $thumb_path = $this->get_thumb_path($this->cfg->thumb_width, $photos[$i], $this->get_latest_version($photos[$i]));
1551
1552
         /* if the image exist and is readable, extract some details */
1553
         if(file_exists($thumb_path) && is_readable($thumb_path)) {
1554
            if($info = getimagesize($thumb_path) !== false) {
1555
               $img_width[$thumbs] = $info[0];
1556
               $img_height[$thumbs] = $info[1];
1557
            }
1558
         }
1559
         $thumbs++;
1560
      }
1561
1562
      if(isset($_SESSION['searchfor_tag']) && $_SESSION['searchfor_tag'] != '')
1563
         $this->tmpl->assign('searchfor_tag', $_SESSION['searchfor_tag']);
1564
1565
      if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1566
         $this->tmpl->assign('from_date', $this->ts2str($_SESSION['from_date']));
1567
         $this->tmpl->assign('to_date', $this->ts2str($_SESSION['to_date']));
1568
      }
1569
1570
      if(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
1571
         $this->tmpl->assign('tag_result', 1);
1572
      }
1573
1574
      /* do we have to display the page selector ? */
1575
      if($this->cfg->thumbs_per_page != 0) {
1576
1577
         $page_select = "";
1578
      
1579
         /* calculate the page switchers */
1580
         $previous_start = $begin_with - $this->cfg->thumbs_per_page;
1581
         $next_start = $begin_with + $this->cfg->thumbs_per_page;
1582
1583
         if($begin_with != 0) 
1584
            $this->tmpl->assign("previous_url", "javascript:showPhotoIndex(". $previous_start .");"); 
1585
         if($end_with < $count)
1586
            $this->tmpl->assign("next_url", "javascript:showPhotoIndex(". $next_start .");"); 
1587
1588
         $photo_per_page  = $this->cfg->thumbs_per_page;
1589
         $last_page = ceil($count / $photo_per_page);
1590
1591
         /* get the current selected page */
1592
         if($begin_with == 0) {
1593
            $current_page = 1;
1594
         } else {
1595
            $current_page = 0;
1596
            for($i = $begin_with; $i >= 0; $i-=$photo_per_page) {
1597
               $current_page++;
1598
            }
1599
         } 
1600
1601
         $dotdot_made = 0;
1602
1603
         for($i = 1; $i <= $last_page; $i++) {
1604
1605
            if($current_page == $i)
1606
               $style = "style=\"font-size: 125%; text-decoration: underline;\"";
1607
            elseif($current_page-1 == $i || $current_page+1 == $i)
1608
               $style = "style=\"font-size: 105%;\"";
1609
            elseif(($current_page-5 >= $i) && ($i != 1) ||
1610
               ($current_page+5 <= $i) && ($i != $last_page))
1611
               $style = "style=\"font-size: 75%;\"";
1612
            else
1613
               $style = "";
1614
1615
            $start_with = ($i*$photo_per_page)-$photo_per_page;
1616
1617
            if($this->is_user_friendly_url()) {
1618
               $select = "<a href=\"". $this->cfg->web_path ."/tag/205/". $start_with ."\"";
1619
            }
1620
            else {
1621
               $select = "<a href=\"". $this->cfg->web_path ."/index.php?mode=showpi&nbsp;tags=". $current_tags ."&nbsp;begin_with=". $begin_with ."\"";
1622
            }
1623
            $select.= " onclick=\"showPhotoIndex(". $start_with ."); return false;\"";
1624
1625
               if($style != "")
1626
                  $select.= $style;
1627
            $select.= ">". $i ."</a>&nbsp;";
1628
1629
            // until 9 pages we show the selector from 1-9
1630
            if($last_page <= 9) {
1631
               $page_select.= $select;
1632
               continue;
1633
            } else {
1634
               if($i == 1 /* first page */ || 
1635
                  $i == $last_page /* last page */ ||
1636
                  $i == $current_page /* current page */ ||
1637
                  $i == ceil($last_page * 0.25) /* first quater */ ||
1638
                  $i == ceil($last_page * 0.5) /* half */ ||
1639
                  $i == ceil($last_page * 0.75) /* third quater */ ||
1640
                  (in_array($i, array(1,2,3,4,5,6)) && $current_page <= 4) /* the first 6 */ ||
1641
                  (in_array($i, array($last_page, $last_page-1, $last_page-2, $last_page-3, $last_page-4, $last_page-5)) && $current_page >= $last_page-4) /* the last 6 */ ||
1642
                  $i == $current_page-3 || $i == $current_page-2 || $i == $current_page-1 /* three before */ ||
1643
                  $i == $current_page+3 || $i == $current_page+2 || $i == $current_page+1 /* three after */) {
1644
1645
                  $page_select.= $select;
1646
                  $dotdot_made = 0;
1647
                  continue;
1648
1649
               }
1650
            }
1651
1652
            if(!$dotdot_made) {
1653
               $page_select.= ".........&nbsp;";
1654
               $dotdot_made = 1;
1655
            }
1656
         }
1657
1658
         /* only show the page selector if we have more then one page */
1659
         if($last_page > 1)
1660
            $this->tmpl->assign('page_selector', $page_select);
1661
      }
1662
      
1663
      $extern_link = "index.php?mode=showpi";
1664
      $rss_link = "index.php?mode=rss";
1665
      if($current_tags != "") {
1666
         $extern_link.= "&tags=". $current_tags;
1667
         $rss_link.= "&tags=". $current_tags;
1668
      }
1669
      if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
1670
         $extern_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1671
         $rss_link.= "&from_date=". $this->ts2str($_SESSION['from_date']) ."&to_date=". $this->ts2str($_SESSION['to_date']);
1672
      }
1673
1674
      $export_link = "index.php?mode=export";
1675
      $slideshow_link = "index.php?mode=slideshow";
1676
1677
      $this->tmpl->assign('extern_link', $extern_link);
1678
      $this->tmpl->assign('slideshow_link', $slideshow_link);
1679
      $this->tmpl->assign('export_link', $export_link);
1680
      $this->tmpl->assign('rss_link', $rss_link);
1681
      $this->tmpl->assign('count', $count);
1682
      $this->tmpl->assign('width', $this->cfg->thumb_width);
1683
      $this->tmpl->assign('preview_width', $this->cfg->photo_width);
1684
      $this->tmpl->assign('thumb_container_width', $this->cfg->thumb_width);
1685
      $this->tmpl->assign('thumb_container_height', $this->cfg->thumb_height+20);
1686
      $this->tmpl->assign('selected_tags', $this->getSelectedTags('img'));
1687
      // +1 for for smarty's selection iteration
1688
      $this->tmpl->assign('thumbs', $thumbs+1);
1689
1690
      if($thumbs > 0) {
1691
         $this->tmpl->assign('images', $images);
1692
         $this->tmpl->assign('img_width', $img_width);
1693
         $this->tmpl->assign('img_height', $img_height);
1694
         $this->tmpl->assign('img_id', $img_id);
1695
         $this->tmpl->assign('img_name', $img_name);
1696
         $this->tmpl->assign('img_fullname', $img_fullname);
1697
         $this->tmpl->assign('img_title', $img_title);
1698
         $this->tmpl->assign('img_rating', $img_rating);
1699
      }
1700
1701
      $result = $this->tmpl->fetch("photo_index.tpl");
1702
1703
      /* if we are returning to photo index from an photo-view,
1704
         scroll the window to the last shown photo-thumbnail.
1705
         after this, unset the last_photo session variable.
1706
      */
1707
      if(isset($_SESSION['last_photo'])) {
1708
         $result.= "<script language=\"JavaScript\">moveToThumb(". $_SESSION['last_photo'] .");</script>\n";
1709
         unset($_SESSION['last_photo']);
1710
      }
1711
1712
      return $result;
1713
1714
   } // showPhotoIndex()
1715
1716
   /**
1717
    * show credit template
1718
    */
1719
   public function showCredits()
1720
   {
1721
      $this->tmpl->assign('version', $this->cfg->version);
1722
      $this->tmpl->assign('product', $this->cfg->product);
1723
      $this->tmpl->assign('db_version', $this->dbver);
1724
      $this->tmpl->show("credits.tpl");
1725
1726
   } // showCredits()
1727
1728
   /**
1729
    * create thumbnails for the requested width
1730
    *
1731
    * this function creates image thumbnails of $orig_image
1732
    * stored as $thumb_image. It will check if the image is
1733
    * in a supported format, if necessary rotate the image
1734
    * (based on EXIF orientation meta headers) and re-sizing.
1735
    * @param string $orig_image
1736
    * @param string $thumb_image
1737
    * @param integer $width
1738
    * @return boolean
1739
    */
1740
   public function create_thumbnail($orig_image, $thumb_image, $width)
1741
   {  
1742
      if(!file_exists($orig_image)) {
1743
         return false;
1744
      }
1745
1746
      $mime = $this->get_mime_info($orig_image);
1747
1748
      /* check if original photo is a support image type */
1749
      if(!$this->checkifImageSupported($mime))
1750
         return false;
1751
1752
      switch($mime) {
1753
1754
         case 'image/jpeg':
1755
1756
            $meta = $this->get_meta_informations($orig_image);
1757
1758
            $rotate = 0;
1759
            $flip_hori = false;
1760
            $flip_vert = false;
1761
1762
            if(isset($meta['Orientation'])) {
1763
               switch($meta['Orientation']) {
1764
                  case 1: /* top, left */
1765
                     /* nothing to do */ break;
1766
                  case 2: /* top, right */
1767
                     $rotate = 0; $flip_hori = true; break;
1768
                  case 3: /* bottom, left */
1769
                     $rotate = 180; break;
1770
                  case 4: /* bottom, right */
1771
                     $flip_vert = true; break;
1772
                  case 5: /* left side, top */
1773
                     $rotate = 90; $flip_vert = true; break;
1774
                  case 6: /* right side, top */
1775
                     $rotate = 90; break;
1776
                  case 7: /* left side, bottom */
1777
                     $rotate = 270; $flip_vert = true; break;
1778
                  case 8: /* right side, bottom */
1779
                     $rotate = 270; break;
1780
               }
1781
            }
1782
1783
            $src_img = @imagecreatefromjpeg($orig_image);
1784
            $handler = "gd";
1785
            break;
1786
1787
         case 'image/png':
1788
1789
            $src_img = @imagecreatefrompng($orig_image);
1790
            $handler = "gd";
1791
            break;
1792
1793
         case 'image/x-portable-pixmap':
1794
1795
            $src_img = new Imagick($orig_image);
1796
            $handler = "imagick";
1797
            break;
1798
1799
      }
1800
1801
      if(!isset($src_img) || empty($src_img)) {
1802
         print "Can't load image from ". $orig_image ."\n";
1803
         return false;
1804
      }
1805
1806
      switch($handler) {
1807
1808
         case 'gd':
1809
1810
            /* grabs the height and width */
1811
            $cur_width = imagesx($src_img);
1812
            $cur_height = imagesy($src_img);
1813
1814
            // If requested width is more then the actual image width,
1815
            // do not generate a thumbnail, instead safe the original
1816
            // as thumbnail but with lower quality. But if the image
1817
            // is to heigh too, then we still have to resize it.
1818
            if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1819
               $result = imagejpeg($src_img, $thumb_image, 75);
1820
               imagedestroy($src_img);
1821
               return true;
1822
            }
1823
            break;
1824
1825
         case 'imagick':
1826
1827
            $cur_width = $src_img->getImageWidth();
1828
            $cur_height = $src_img->getImageHeight();
1829
1830
            // If requested width is more then the actual image width,
1831
            // do not generate a thumbnail, instead safe the original
1832
            // as thumbnail but with lower quality. But if the image
1833
            // is to heigh too, then we still have to resize it.
1834
            if($width >= $cur_width && $cur_height < $this->cfg->thumb_height) {
1835
               $src_img->setCompressionQuality(75);
1836
               $src_img->setImageFormat('jpeg');
1837
               $src_img->writeImage($thumb_image);
1838
               $src_img->clear();
1839
               $src_img->destroy();
1840
               return true;
1841
            }
1842
            break;
1843
1844
      }
1845
1846
      // If the image will be rotate because EXIF orientation said so
1847
      // 'virtually rotate' the image for further calculations
1848
      if($rotate == 90 || $rotate == 270) {
1849
         $tmp = $cur_width;
1850
         $cur_width = $cur_height;
1851
         $cur_height = $tmp;
1852
      }
1853
1854
      /* calculates aspect ratio */
1855
      $aspect_ratio = $cur_height / $cur_width;
1856
1857
      /* sets new size */
1858
      if($aspect_ratio < 1) {
1859
         $new_w = $width;
1860
         $new_h = abs($new_w * $aspect_ratio);
1861
      } else {
1862
         /* 'virtually' rotate the image and calculate it's ratio */
1863
         $tmp_w = $cur_height;
1864
         $tmp_h = $cur_width;
1865
         /* now get the ratio from the 'rotated' image */
1866
         $tmp_ratio = $tmp_h/$tmp_w;
1867
         /* now calculate the new dimensions */
1868
         $tmp_w = $width;
1869
         $tmp_h = abs($tmp_w * $tmp_ratio);
1870
1871
         // now that we know, how high they photo should be, if it
1872
         // gets rotated, use this high to scale the image
1873
         $new_h = $tmp_h;
1874
         $new_w = abs($new_h / $aspect_ratio);
1875
1876
         // If the image will be rotate because EXIF orientation said so
1877
         // now 'virtually rotate' back the image for the image manipulation
1878
         if($rotate == 90 || $rotate == 270) {
1879
            $tmp = $new_w;
1880
            $new_w = $new_h;
1881
            $new_h = $tmp;
1882
         }
1883
      }
1884
1885
      switch($handler) {
1886
1887
         case 'gd':
1888
1889
            /* creates new image of that size */
1890
            $dst_img = imagecreatetruecolor($new_w, $new_h);
1891
1892
            imagefill($dst_img, 0, 0, ImageColorAllocate($dst_img, 255, 255, 255));
1893
1894
            /* copies resized portion of original image into new image */
1895
            imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $new_w, $new_h, imagesx($src_img), imagesy($src_img));
1896
1897
            /* needs the image to be flipped horizontal? */
1898
            if($flip_hori) {
1899
               $this->_debug("(FLIP)");
1900
               $dst_img = $this->flipImage($dst_img, 'hori');
1901
            }
1902
            /* needs the image to be flipped vertical? */
1903
            if($flip_vert) {
1904
               $this->_debug("(FLIP)");
1905
               $dst_img = $this->flipImage($dst_img, 'vert');
1906
            }
1907
1908
            if($rotate) {
1909
               $this->_debug("(ROTATE)");
1910
               $dst_img = $this->rotateImage($dst_img, $rotate);
1911
            }
1912
1913
            /* write down new generated file */
1914
            $result = imagejpeg($dst_img, $thumb_image, 75);
1915
1916
            /* free your mind */
1917
            imagedestroy($dst_img);
1918
            imagedestroy($src_img);
1919
1920
            if($result === false) {
1921
               print "Can't write thumbnail ". $thumb_image ."\n";
1922
               return false;
1923
            }
1924
1925
            return true;
1926
1927
            break;
1928
1929
         case 'imagick':
1930
1931
            $src_img->resizeImage($new_w, $new_h, Imagick::FILTER_LANCZOS, 1);
1932
1933
            /* needs the image to be flipped horizontal? */
1934
            if($flip_hori) {
1935
               $this->_debug("(FLIP)");
1936
               $src_img->rotateImage(new ImagickPixel(), 90);
1937
               $src_img->flipImage();
1938
               $src_img->rotateImage(new ImagickPixel(), -90);
1939
            }
1940
            /* needs the image to be flipped vertical? */
1941
            if($flip_vert) {
1942
               $this->_debug("(FLIP)");
1943
               $src_img->flipImage();
1944
            }
1945
1946
            if($rotate) {
1947
               $this->_debug("(ROTATE)");
1948
               $src_img->rotateImage(new ImagickPixel(), $rotate);
1949
            }
1950
1951
            $src_img->setCompressionQuality(75);
1952
            $src_img->setImageFormat('jpeg');
1953
1954
            if(!$src_img->writeImage($thumb_image)) {
1955
               print "Can't write thumbnail ". $thumb_image ."\n";
1956
               return false;
1957
            }
1958
1959
            $src_img->clear();
1960
            $src_img->destroy();
1961
            return true;
1962
1963
            break;
1964
1965
      }
1966
1967
   } // create_thumbnail()
1968
1969
   /**
1970
    * return all exif meta data from the file
1971
    * @param string $file
1972
    * @return array
1973
    */
1974
   public function get_meta_informations($file)
1975
   {
1976
      return exif_read_data($file);
1977
1978
   } // get_meta_informations()
1979
1980
   /**
1981
    * create phpfspot own sqlite database
1982
    *
1983
    * this function creates phpfspots own sqlite database
1984
    * if it does not exist yet. this own is used to store
1985
    * some necessary informations (md5 sum's, ...).
1986
    */
1987
   public function check_phpfspot_db()
1988
   {
1989
      // if the config table doesn't exist yet, create it
1990
      if(!$this->cfg_db->db_check_table_exists("images")) {
1991
         $this->cfg_db->db_exec("
1992
            CREATE TABLE images (
1993
               img_idx int,
1994
               img_version_idx int,
1995
               img_md5 varchar(32),
1996
               UNIQUE(img_idx, img_version_idx)
1997
            )
1998
         ");
1999
      }
2000
2001
      if(!$this->cfg_db->db_check_table_exists("meta")) {
2002
         $this->cfg_db->db_exec("
2003
            CREATE TABLE meta (
2004
               meta_key varchar(255),
2005
               meta_value varchar(255)
2006
            )
2007
         ");
2008
2009
         /* db_version was added with phpfspot 1.7, before changes
2010
            on the phpfspot database where not necessary.
2011
         */
2012
2013
         $this->cfg_db->db_exec("
2014
            INSERT INTO meta (
2015
               meta_key, meta_value
2016
            ) VALUES (
2017
               'phpfspot Database Version',
2018
               '". $this->cfg->db_version ."'
2019
            )
2020
         ");
2021
      }
2022
2023
      /* if version <= 2 and column img_version_idx does not exist yet */
2024
      if($this->get_db_version() <= 2 &&
2025
         !$this->cfg_db->db_check_column_exists("images", "img_version_idx")) {
2026
2027
         if(!$this->cfg_db->db_start_transaction())
2028
            die("Can not start database transaction");
2029
2030
         $result = $this->cfg_db->db_exec("
2031
            CREATE TEMPORARY TABLE images_temp (
2032
               img_idx int,
2033
               img_version_idx int,
2034
               img_md5 varchar(32),
2035
               UNIQUE(img_idx, img_version_idx)
2036
            )
2037
         ");
2038
2039
         if(!$result) {
2040
            $this->cfg_db->db_rollback_transaction();
2041
            die("Upgrade failed - transaction rollback");
2042
         }
2043
2044
         $result = $this->cfg_db->db_exec("
2045
           INSERT INTO images_temp
2046
               SELECT
2047
                  img_idx,
2048
                  0,
2049
                  img_md5
2050
               FROM images
2051
         ");
2052
2053
         if(!$result) {
2054
            $this->cfg_db->db_rollback_transaction();
2055
            die("Upgrade failed - transaction rollback");
2056
         }
2057
2058
         $result = $this->cfg_db->db_exec("
2059
            DROP TABLE images
2060
         ");
2061
2062
         if(!$result) {
2063
            $this->cfg_db->db_rollback_transaction();
2064
            die("Upgrade failed - transaction rollback");
2065
         }
2066
2067
         $result = $this->cfg_db->db_exec("
2068
            CREATE TABLE images (
2069
               img_idx int,
2070
               img_version_idx int,
2071
               img_md5 varchar(32),
2072
               UNIQUE(img_idx, img_version_idx)
2073
            )
2074
         ");
2075
2076
         if(!$result) {
2077
            $this->cfg_db->db_rollback_transaction();
2078
            die("Upgrade failed - transaction rollback");
2079
         }
2080
2081
         $result = $this->cfg_db->db_exec("
2082
            INSERT INTO images
2083
               SELECT *
2084
               FROM images_temp
2085
         ");
2086
2087
         if(!$result) {
2088
            $this->cfg_db->db_rollback_transaction();
2089
            die("Upgrade failed - transaction rollback");
2090
         }
2091
2092
         $result = $this->cfg_db->db_exec("
2093
            DROP TABLE images_temp
2094
         ");
2095
2096
         if(!$result) {
2097
            $this->cfg_db->db_rollback_transaction();
2098
            die("Upgrade failed - transaction rollback");
2099
         }
2100
2101
         if(!$this->cfg_db->db_commit_transaction())
2102
            die("Can not commit database transaction");
2103
2104
      }
2105
2106
   } // check_phpfspot_db
2107
2108
   /**
2109
    * generates thumbnails
2110
    *
2111
    * This function generates JPEG thumbnails from
2112
    * provided F-Spot photo indize and its alternative
2113
    * versions.
2114
    *
2115
    * 1. Check if all thumbnail generations (width) are already in place and
2116
    *    readable
2117
    * 2. Check if the md5sum of the original file has changed
2118
    * 3. Generate the thumbnails if needed
2119
    * @param integer $idx
2120
    * @param integer $force
2121
    * @param boolean $overwrite
2122
    */
2123
   public function gen_thumb($idx = 0, $force = 0, $overwrite = false)
2124
   {
2125
      $error = 0;
2126
      $versions = Array(0);
2127
2128
      $resolutions = Array(
2129
         $this->cfg->thumb_width,
2130
         $this->cfg->photo_width,
2131
         $this->cfg->mini_width,
2132
         30,
2133
      );
2134
2135
      /* in newer f-spot versions, also the original photos is listed
2136
         in photo_versions with version_id 1, name 'Original'. We have
2137
         to skip that one.
2138
      */
2139
      if($alt_versions = $this->get_photo_versions($idx)) {
2140
2141
         if($this->getFspotDBVersion() < 18) {
2142
            $versions = array_merge($versions, $alt_versions);
2143
         }
2144
         else {
2145
            unset($alt_versions[0]);
2146
            if(!empty($alt_versions))
2147
               $versions = array_merge($versions, $alt_versions);
2148
         }
2149
      }
2150
2151
      foreach($versions as $version) {
2152
2153
         /* get details from F-Spot's database */
2154
         $details = $this->get_photo_details($idx, $version);
2155
2156
         /* calculate file MD5 sum */
2157
         $full_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2158
2159
         if(!file_exists($full_path)) {
2160
            $this->_error("File ". $full_path ." does not exist");
2161
            return;
2162
         }
2163
2164
         if(!is_readable($full_path)) {
2165
            $this->_error("File ". $full_path ." is not readable for ". $this->getuid() ."\n");
2166
            return;
2167
         }
2168
2169
         $this->_debug("Image [". $idx ."] ". $this->shrink_text($this->parse_uri($details['uri'], 'filename'), 20) ." Thumbnails:");
2170
2171
         /* If Nikon NEF format, we need to treat it another way */
2172
         if(isset($this->cfg->dcraw_bin) &&
2173
            file_exists($this->cfg->dcraw_bin) &&
2174
            is_executable($this->cfg->dcraw_bin) &&
2175
            preg_match('/\.nef$/i', $details['uri'])) {
2176
2177
            $ppm_path = preg_replace('/\.nef$/i', '.ppm', $full_path);
2178
2179
            /* if PPM file does not exist, let dcraw convert it from NEF */
2180
            if(!file_exists($ppm_path)) {
2181
               system($this->cfg->dcraw_bin ." -a ". $full_path);
2182
            }
2183
2184
            /* for now we handle the PPM instead of the NEF */
2185
            $full_path = $ppm_path;
2186
2187
         }
2188
2189
         $file_md5 = md5_file($full_path);
2190
         $changes = false;
2191
2192
         foreach($resolutions as $resolution) {
2193
2194
            $generate_it = false;
2195
2196
            $thumb_sub_path = substr($file_md5, 0, 2);
2197
            $thumb_path = $this->cfg->thumb_path ."/". $thumb_sub_path ."/". $resolution ."_". $file_md5;
2198
2199
            /* if thumbnail-subdirectory does not exist yet, create it */
2200
            if(!file_exists(dirname($thumb_path))) {
2201
               mkdir(dirname($thumb_path), 0755);
2202
            }
2203
2204
            /* if the thumbnail file doesn't exist, create it */
2205
            if(!file_exists($thumb_path) || $force) {
2206
               $generate_it = true;
2207
            }
2208
            elseif($file_md5 != $this->getMD5($idx, $version)) {
2209
               $generate_it = true;
2210
            }
2211
2212
            if($generate_it || $overwrite) {
2213
2214
               $this->_debug(" ". $resolution ."px");
2215
               if(!$this->create_thumbnail($full_path, $thumb_path, $resolution))
2216
                  $error = 1;
2217
2218
               $changes = true;
2219
            }
2220
         }
2221
2222
         if(empty($changes)) {
2223
            $this->_debug(" already exist");
2224
         }
2225
2226
         /* set the new/changed MD5 sum for the current photo */
2227
         if(!$error) {
2228
            $this->setMD5($idx, $file_md5, $version);
2229
         }
2230
2231
         $this->_debug("\n");
2232
2233
      }
2234
2235
   } // gen_thumb()
2236
2237
   /**
2238
    * returns stored md5 sum for a specific photo
2239
    *
2240
    * this function queries the phpfspot database for a stored MD5
2241
    * checksum of the specified photo. It also takes care of the
2242
    * requested photo version - original or alternative photo.
2243
    *
2244
    * @param integer $idx
2245
    * @return string|null
2246
    */
2247
   public function getMD5($idx, $version_idx = 0)
2248
   {
2249
      $result = $this->cfg_db->db_query("
2250
         SELECT
2251
            img_md5
2252
         FROM
2253
            images
2254
         WHERE
2255
            img_idx='". $idx ."'
2256
         AND
2257
            img_version_idx='". $version_idx ."'
2258
      ");
2259
2260
      if(!$result)
2261
         return NULL;
2262
2263
      if($img = $this->cfg_db->db_fetch_object($result))
2264
         return $img['img_md5'];
2265
2266
      return NULL;
2267
      
2268
   } // getMD5()
2269
2270
   /**
2271
    * set MD5 sum for the specific photo
2272
    * @param integer $idx
2273
    * @param string $md5
2274
    */
2275
   private function setMD5($idx, $md5, $version_idx = 0)
2276
   {
2277
      $result = $this->cfg_db->db_exec("
2278
         INSERT OR REPLACE INTO images (
2279
            img_idx, img_version_idx, img_md5
2280
         ) VALUES (
2281
            '". $idx ."',
2282
            '". $version_idx ."',
2283
            '". $md5 ."'
2284
         )
2285
      ");
2286
2287
   } // setMD5()
2288
2289
   /**
2290
    * store current tag condition
2291
    *
2292
    * this function stores the current tag condition
2293
    * (AND or OR) in the users session variables
2294
    * @param string $mode
2295
    * @return string
2296
    */
2297
   public function setTagCondition($mode)
2298
   {
2299
      $_SESSION['tag_condition'] = $mode;
2300
2301
      return "ok";
2302
2303
   } // setTagCondition()
2304
2305
   /** 
2306
    * invoke tag & date search 
2307
    *
2308
    * this function will return all matching tags and store
2309
    * them in the session variable selected_tags. furthermore
2310
    * it also handles the date search.
2311
    * getPhotoSelection() will then only return the matching
2312
    * photos.
2313
    * @return string
2314
    */
2315
   public function startSearch()
2316
   {
2317
      /* date search */
2318
      if(isset($_POST['date_from']) && $this->isValidDate($_POST['date_from'])) {
2319
         $date_from = $_POST['date_from'];
2320
      }
2321
      if(isset($_POST['date_to']) && $this->isValidDate($_POST['date_to'])) {
2322
         $date_to = $_POST['date_to'];
2323
      }
2324
2325
      /* tag-name search */
2326
      if(isset($_POST['for_tag']) && is_string($_POST['for_tag'])) {
2327
         $searchfor_tag = $_POST['for_tag'];
2328
         $_SESSION['searchfor_tag'] = $_POST['for_tag'];
2329
      }
2330
      else {
2331
         unset($_SESSION['searchfor_tag']);
2332
      }
2333
2334
      /* file-name search */
2335
      if(isset($_POST['for_name']) && is_string($_POST['for_name'])) {
2336
         $_SESSION['searchfor_name'] = $_POST['for_name'];
2337
      }
2338
      else {
2339
         unset($_SESSION['searchfor_name']);
2340
      }
2341
2342
      /* rate-search */
2343
      if(isset($_POST['rate_from']) && is_numeric($_POST['rate_from'])) {
2344
2345
         $_SESSION['rate_from'] = $_POST['rate_from'];
2346
2347
         if(isset($_POST['rate_to']) && is_numeric($_POST['rate_to'])) {
2348
            $_SESSION['rate_to'] = $_POST['rate_to'];
2349
         }
2350
      }
2351
      else {
2352
         /* delete any previously set value */
2353
         unset($_SESSION['rate_to'], $_SESSION['rate_from']);
2354
      }
2355
2356
      $this->get_tags();
2357
2358
      if(isset($date_from) && !empty($date_from))
2359
         $_SESSION['from_date'] = strtotime($date_from ." 00:00:00");
2360
      else
2361
         unset($_SESSION['from_date']);
2362
2363
      if(isset($date_to) && !empty($date_to))
2364
         $_SESSION['to_date'] = strtotime($date_to ." 23:59:59");
2365
      else
2366
         unset($_SESSION['to_date']);
2367
2368
      if(isset($searchfor_tag) && !empty($searchfor_tag)) {
2369
         /* new search, reset the current selected tags */
2370
         $_SESSION['selected_tags'] = Array();
2371
         foreach($this->avail_tags as $tag) {
2372
            if(preg_match('/'. $searchfor_tag .'/i', $this->tags[$tag]))
2373
               array_push($_SESSION['selected_tags'], $tag);
2374
         }
2375
      }
2376
2377
      return "ok";
2378
2379
   } // startSearch()
2380
2381
   /**
2382
    * updates sort order in session variable
2383
    *
2384
    * this function is invoked by RPC and will sort the requested
2385
    * sort order in the session variable.
2386
    * @param string $sort_order
2387
    * @return string
2388
    */
2389
   public function updateSortOrder($order)
2390
   {
2391
      if(isset($this->sort_orders[$order])) {
2392
         $_SESSION['sort_order'] = $order;
2393
         return "ok";
2394
      }
2395
2396
      return "unkown error";
2397
2398
   } // updateSortOrder()
2399
2400
   /**
2401
    * update photo version in session variable
2402
    *
2403
    * this function is invoked by RPC and will set the requested
2404
    * photo version in the session variable.
2405
    * @param string $photo_version
2406
    * @return string
2407
    */
2408
   public function update_photo_version($photo_idx, $photo_version)
2409
   {
2410
      if($this->is_valid_version($photo_idx, $photo_version)) {
2411
         $_SESSION['current_version'] = $photo_version;
2412
         return "ok";
2413
      }
2414
2415
      return "incorrect photo version provided";
2416
2417
   } // update_photo_version()
2418
2419
   /**
2420
    * rotate image
2421
    *
2422
    * this function rotates the image according the
2423
    * specified angel.
2424
    * @param string $img
2425
    * @param integer $degress
2426
    * @return image
2427
    */
2428
   private function rotateImage($img, $degrees)
2429
   {
2430
      if(function_exists("imagerotate")) {
2431
         $img = imagerotate($img, $degrees, 0);
2432
      } else {
2433
         function imagerotate($src_img, $angle)
2434
         {
2435
            $src_x = imagesx($src_img);
2436
            $src_y = imagesy($src_img);
2437
            if ($angle == 180) {
2438
               $dest_x = $src_x;
2439
               $dest_y = $src_y;
2440
            }
2441
            elseif ($src_x <= $src_y) {
2442
               $dest_x = $src_y;
2443
               $dest_y = $src_x;
2444
            }
2445
            elseif ($src_x >= $src_y) {
2446
               $dest_x = $src_y;
2447
               $dest_y = $src_x;
2448
            }
2449
               
2450
            $rotate=imagecreatetruecolor($dest_x,$dest_y);
2451
            imagealphablending($rotate, false);
2452
               
2453
            switch ($angle) {
2454
            
2455
               case 90:
2456
                  for ($y = 0; $y < ($src_y); $y++) {
2457
                     for ($x = 0; $x < ($src_x); $x++) {
2458
                        $color = imagecolorat($src_img, $x, $y);
2459
                        imagesetpixel($rotate, $dest_x - $y - 1, $x, $color);
2460
                     }
2461
                  }
2462
                  break;
2463
2464
               case 270:
2465
                  for ($y = 0; $y < ($src_y); $y++) {
2466
                     for ($x = 0; $x < ($src_x); $x++) {
2467
                        $color = imagecolorat($src_img, $x, $y);
2468
                        imagesetpixel($rotate, $y, $dest_y - $x - 1, $color);
2469
                     }
2470
                  }
2471
                  break;
2472
2473
               case 180:
2474
                  for ($y = 0; $y < ($src_y); $y++) {
2475
                     for ($x = 0; $x < ($src_x); $x++) {
2476
                        $color = imagecolorat($src_img, $x, $y);
2477
                        imagesetpixel($rotate, $dest_x - $x - 1, $dest_y - $y - 1, $color);
2478
                     }
2479
                  }
2480
                  break;
2481
2482
               default:
2483
                  $rotate = $src_img;
2484
                  break;
2485
            };
2486
2487
            return $rotate;
2488
2489
         }
2490
2491
         $img = imagerotate($img, $degrees);
2492
2493
      }
2494
2495
      return $img;
2496
2497
   } // rotateImage()
2498
2499
   /**
2500
    * returns flipped image
2501
    *
2502
    * this function will return an either horizontal or
2503
    * vertical flipped truecolor image.
2504
    * @param string $image
2505
    * @param string $mode 
2506
    * @return image
2507
    */
2508
   private function flipImage($image, $mode)
2509
   {
2510
      $w = imagesx($image);
2511
      $h = imagesy($image);
2512
      $flipped = imagecreatetruecolor($w, $h);
2513
2514
      switch($mode) {
2515
         case 'vert':
2516
            for ($y = 0; $y < $h; $y++) {
2517
               imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1);
2518
            }
2519
            break;
2520
         case 'hori':
2521
            for ($x = 0; $x < $w; $x++) {
2522
               imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h);
2523
            }
2524
            break;
2525
      }
2526
2527
      return $flipped;
2528
2529
   } // flipImage()
2530
2531
   /**
2532
    * return all assigned tags for the specified photo
2533
    * @param integer $idx
2534
    * @return array
2535
    */
2536
   private function get_photo_tags($idx)
2537
   {
2538
      $result = $this->db->db_query("
2539
         SELECT t.id as id, t.name as name
2540
         FROM tags t
2541
         INNER JOIN photo_tags pt
2542
            ON t.id=pt.tag_id
2543
         WHERE pt.photo_id='". $idx ."'
2544
      ");
2545
2546
      $tags = Array();
2547
2548
      while($row = $this->db->db_fetch_object($result)) {
2549
         if(isset($this->cfg->hide_tags) && in_array($row['name'], $this->cfg->hide_tags))
2550
            continue;
2551
         $tags[$row['id']] = $row['name'];
2552
      }
2553
2554
      return $tags;
2555
2556
   } // get_photo_tags()
2557
2558
   /**
2559
    * create on-the-fly images with text within
2560
    * @param string $txt
2561
    * @param string $color
2562
    * @param integer $space
2563
    * @param integer $font
2564
    * @param integer $w
2565
    */
2566
   public function showTextImage($txt, $color=000000, $space=4, $font=4, $w=300)
2567
   {
2568
      if (strlen($color) != 6) 
2569
         $color = 000000;
2570
2571
      $int = hexdec($color);
2572
      $h = imagefontheight($font);
2573
      $fw = imagefontwidth($font);
2574
      $txt = explode("\n", wordwrap($txt, ($w / $fw), "\n"));
2575
      $lines = count($txt);
2576
      $im = imagecreate($w, (($h * $lines) + ($lines * $space)));
2577
      $bg = imagecolorallocate($im, 255, 255, 255);
2578
      $color = imagecolorallocate($im, 0xFF & ($int >> 0x10), 0xFF & ($int >> 0x8), 0xFF & $int);
2579
      $y = 0;
2580
2581
      foreach ($txt as $text) {
2582
         $x = (($w - ($fw * strlen($text))) / 2);
2583
         imagestring($im, $font, $x, $y, $text, $color);
2584
         $y += ($h + $space);
2585
      }
2586
2587
      Header("Content-type: image/png");
2588
      ImagePng($im);
2589
2590
   } // showTextImage()
2591
2592
   /**
2593
    * check if all requirements are met
2594
    * @return boolean
2595
    */
2596
   private function check_requirements()
2597
   {
2598
      if(!function_exists("imagecreatefromjpeg")) {
2599
         print "PHP GD library extension is missing<br />\n";
2600
         $missing = true;
2601
      }
2602
2603
      if($this->cfg->db_access == "native" && !function_exists("sqlite3_open")) {
2604
         print "PHP SQLite3 library extension is missing<br />\n";
2605
         $missing = true;
2606
      }
2607
2608
      if($this->cfg->db_access == "pdo") {
2609
         if(array_search("sqlite", PDO::getAvailableDrivers()) === false) {
2610
            print "PDO SQLite3 driver is missing<br />\n";
2611
            $missing = true;
2612
         }
2613
      }
2614
2615
      /* Check for HTML_AJAX PEAR package, lent from Horde project */
2616
      ini_set('track_errors', 1);
2617
      @include_once 'HTML/AJAX/Server.php';
2618
      if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2619
         print "PEAR HTML_AJAX package is missing<br />\n";
2620
         $missing = true;
2621
         unset($php_errormsg);
2622
      }
2623
      @include_once 'Calendar/Calendar.php';
2624
      if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2625
         print "PEAR Calendar package is missing<br />\n";
2626
         $missing = true;
2627
         unset($php_errormsg);
2628
      }
2629
      @include_once 'Console/Getopt.php';
2630
      if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2631
         print "PEAR Console_Getopt package is missing<br />\n";
2632
         $missing = true;
2633
         unset($php_errormsg);
2634
      }
2635
      @include_once 'Date.php';
2636
      if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2637
         print "PEAR Date package is missing<br />\n";
2638
         $missing = true;
2639
         unset($php_errormsg);
2640
      }
2641
      @include_once $this->cfg->smarty_path .'/libs/Smarty.class.php';
2642
      if(isset($php_errormsg) && preg_match('/Failed opening.*for inclusion/i', $php_errormsg)) {
2643
         print "Smarty template engine can not be found in ". $this->cfg->smarty_path ."/libs/Smarty.class.php<br />\n";
2644
         $missing = true;
2645
         unset($php_errormsg);
2646
      }
2647
      ini_restore('track_errors');
2648
2649
      if(isset($missing))
2650
         return false;
2651
2652
      return true;
2653
2654
   } // check_requirements()
2655
2656
   private function _debug($text)
2657
   {
2658
      if(isset($this->fromcmd)) {
2659
         print $text;
2660
      }
2661
2662
   } // _debug()
2663
2664
   /**
2665
    * check if specified MIME type is supported
2666
    * @param string $mime
2667
    * @return boolean
2668
    */
2669
   public function checkifImageSupported($mime)
2670
   {
2671
      $supported_types =  Array(
2672
         "image/jpeg",
2673
         "image/png",
2674
         "image/x-portable-pixmap",
2675
         "image/tiff"
2676
      );
2677
2678
      if(in_array($mime, $supported_types))
2679
         return true;
2680
2681
      return false;
2682
2683
   } // checkifImageSupported()
2684
2685
   /**
2686
    * output error text
2687
    * @param string $text
2688
    */
2689
   public function _error($text)
2690
   {
2691
      switch($this->cfg->logging) {
2692
         default:
2693
         case 'display':
2694
            if(isset($this->fromcmd))
2695
               print $text ."\n";
2696
            else {
2697
               print "<img src=\"resources/green_info.png\" alt=\"warning\" />\n";
2698
               print $text ."<br />\n";
2699
            }
2700
            break;
2701
         case 'errorlog':  
2702
            error_log($text);
2703
            break;
2704
         case 'logfile':
2705
            error_log($text, 3, $this->cfg->log_file);
2706
            break;
2707
      }
2708
2709
      $this->runtime_error = true;
2710
2711
   } // _error()
2712
2713
   /**
2714
    * get calendar input-text fields
2715
    *
2716
    * this function returns a text-field used for the data selection.
2717
    * Either it will be filled with the current date or, if available,
2718
    * filled with the date user entered previously.
2719
    *
2720
    * @param string $mode
2721
    * @return string
2722
    */
2723
   private function get_date_text_field($mode)
2724
   {
2725
      $date = isset($_SESSION[$mode .'_date']) ? date("Y", $_SESSION[$mode .'_date']) : date("Y");
2726
      $date.= "-";
2727
      $date.= isset($_SESSION[$mode .'_date']) ? date("m", $_SESSION[$mode .'_date']) : date("m");
2728
      $date.= "-";
2729
      $date.= isset($_SESSION[$mode .'_date']) ? date("d", $_SESSION[$mode .'_date']) : date("d");
2730
2731
      $output = "<input type=\"text\" size=\"15\" id=\"date_". $mode ."\" value=\"". $date ."\"";
2732
      if(!isset($_SESSION[$mode .'_date']))
2733
         $output.= " disabled=\"disabled\"";
2734
      $output.= " />\n";
2735
2736
      return $output;
2737
2738
   } // get_date_text_field()
2739
2740
   /**
2741
    * output calendar matrix
2742
    * @param integer $year
2743
    * @param integer $month
2744
    * @param integer $day
2745
    */
2746
   public function get_calendar_matrix($userdate)
2747
   {
2748
      if(($userdate = strtotime($userdate)) === false) {
2749
         $year = date('Y');
2750
         $month = date('m');
2751
         $day = date('d');
2752
      }
2753
      else {
2754
         $date = new Date();
2755
         $date->setDate($userdate);
2756
2757
         $year  = $date->getYear();
2758
         $month = $date->getMonth();
2759
         $day   = $date->getDay();
2760
      }
2761
2762
      $rows = 1;
2763
      $cols = 1;
2764
      $matrix = Array();
2765
2766
      require_once CALENDAR_ROOT.'Month/Weekdays.php';
2767
      require_once CALENDAR_ROOT.'Day.php';
2768
2769
      // Build the month
2770
      $month_cal = new Calendar_Month_Weekdays($year,$month);
2771
2772
      // Create links
2773
      $prevStamp = $month_cal->prevMonth(true);
2774
      $prev = "javascript:setMonth(". date('Y',$prevStamp) .", ". date('n',$prevStamp) .", ". date('j',$prevStamp) .");";
2775
      $nextStamp = $month_cal->nextMonth(true);
2776
      $next = "javascript:setMonth(". date('Y',$nextStamp) .", ". date('n',$nextStamp) .", ". date('j',$nextStamp) .");";
2777
2778
      $selectedDays = array (
2779
         new Calendar_Day($year,$month,$day),
2780
      );
2781
2782
      // Build the days in the month
2783
      $month_cal->build($selectedDays);
2784
2785
      $this->tmpl->assign('current_month', date('F Y',$month_cal->getTimeStamp()));
2786
      $this->tmpl->assign('prev_month', $prev);
2787
      $this->tmpl->assign('next_month', $next);
2788
2789
      while ( $day = $month_cal->fetch() ) {
2790
   
2791
         if(!isset($matrix[$rows]))
2792
            $matrix[$rows] = Array();
2793
2794
         $string = "";
2795
2796
         $dayStamp = $day->thisDay(true);
2797
         $link = "javascript:setCalendarDate('"
2798
            . date('Y',$dayStamp)
2799
            . "-"
2800
            . date('m',$dayStamp)
2801
            . "-"
2802
            . date('d',$dayStamp)
2803
            ."');";
2804
2805
         // isFirst() to find start of week
2806
         if ( $day->isFirst() )
2807
            $string.= "<tr>\n";
2808
2809
         if ( $day->isSelected() ) {
2810
            $string.= "<td class=\"selected\">".$day->thisDay()."</td>\n";
2811
         } else if ( $day->isEmpty() ) {
2812
            $string.= "<td>&nbsp;</td>\n";
2813
         } else {
2814
            $string.= "<td><a class=\"calendar\" href=\"".$link."\">".$day->thisDay()."</a></td>\n";
2815
         }
2816
2817
         // isLast() to find end of week
2818
         if ( $day->isLast() )
2819
            $string.= "</tr>\n";
2820
2821
         $matrix[$rows][$cols] = $string;
2822
2823
         $cols++;
2824
2825
         if($cols > 7) {
2826
            $cols = 1;
2827
            $rows++;
2828
         }
2829
      }
2830
2831
      $this->tmpl->assign('matrix', $matrix);
2832
      $this->tmpl->assign('rows', $rows);
2833
      $this->tmpl->show("calendar.tpl");
2834
2835
   } // get_calendar_matrix()
2836
2837
   /**
2838
    * output export page
2839
    * @param string $mode
2840
    */
2841
   public function getExport($mode)
2842
   {
2843
      $pictures = $this->getPhotoSelection();
2844
      $current_tags = $this->getCurrentTags();  
2845
2846
      foreach($pictures as $picture) {
2847
2848
         $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2849
         if($current_tags != "") {
2850
            $orig_url.= "&tags=". $current_tags;
2851
         } 
2852
         if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2853
            $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2854
         }
2855
2856
         if($this->is_user_friendly_url()) {
2857
            $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2858
         }
2859
         else {
2860
            $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2861
         }
2862
2863
         switch($mode) {
2864
2865
            case 'HTML':
2866
               // <a href="%pictureurl%"><img src="%thumbnailurl%" ></a>
2867
               print htmlspecialchars("<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>") ."<br />\n";
2868
               break;
2869
               
2870
            case 'MoinMoin':
2871
               // "[%pictureurl% %thumbnailurl%]"
2872
               print htmlspecialchars("[".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2873
               break;
2874
2875
            case 'MoinMoinList':
2876
               // " * [%pictureurl% %thumbnailurl%]"
2877
               print "&nbsp;" . htmlspecialchars("* [".$orig_url." ".$thumb_url."&fake=1.jpg]") ."<br />\n";
2878
               break;
2879
         }
2880
2881
      }
2882
2883
   } // getExport()
2884
2885
   /**
2886
    * output RSS feed
2887
    */
2888
   public function getRSSFeed()
2889
   {
2890
      Header("Content-type: text/xml; charset=utf-8");
2891
      print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
2892
?>
2893
<rss version="2.0"
2894
   xmlns:media="http://search.yahoo.com/mrss/"
2895
   xmlns:dc="http://purl.org/dc/elements/1.1/"
2896
 >
2897
 <channel>
2898
  <title>phpfspot</title>
2899
  <description>phpfspot RSS feed</description>
2900
  <link><?php print htmlspecialchars($this->get_phpfspot_url()); ?></link>
2901
  <pubDate><?php print strftime("%a, %d %b %Y %T %z"); ?></pubDate>
2902
  <generator>phpfspot</generator>
2903
<?php
2904
2905
      $pictures = $this->getPhotoSelection();
2906
      $current_tags = $this->getCurrentTags();  
2907
2908
      foreach($pictures as $picture) {
2909
2910
         $orig_url = $this->get_phpfspot_url() ."/index.php?mode=showp&id=". $picture;
2911
         if($current_tags != "") {
2912
            $orig_url.= "&tags=". $current_tags;
2913
         } 
2914
         if(isset($_SESSION['from_date']) && isset($_SESSION['to_date'])) {
2915
            $orig_url.= "&from_date=". $_SESSION['from_date'] ."&to_date=". $_SESSION['to_date'];
2916
         }
2917
2918
         $details = $this->get_photo_details($picture);
2919
2920
         if($this->is_user_friendly_url()) {
2921
            $thumb_url = $this->get_phpfspot_url() ."/photo/". $picture ."/". $this->cfg->thumb_width;
2922
         }
2923
         else {
2924
            $thumb_url = $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $picture ."&width=". $this->cfg->thumb_width;
2925
         }
2926
2927
         $thumb_html = htmlspecialchars("
2928
<a href=\"". $orig_url ."\"><img src=\"". $thumb_url ."\" /></a>
2929
<br>
2930
". $details['description']);
2931
2932
         $orig_path = $this->translate_path($this->parse_uri($details['uri'], 'fullpath'));
2933
2934
         /* get EXIF information if JPEG */
2935
         if(isset($details['mime']) && $details['mime'] == "image/jpeg") {
2936
            $meta = $this->get_meta_informations($orig_path);
2937
         }
2938
2939
?>
2940
  <item>
2941
   <title><?php print htmlspecialchars($this->parse_uri($details['uri'], 'filename')); ?></title>
2942
   <link><?php print htmlspecialchars($orig_url); ?></link>
2943
   <guid><?php print htmlspecialchars($orig_url); ?></guid>
2944
   <dc:date.Taken><?php print strftime("%Y-%m-%dT%H:%M:%S+00:00", $details['time']); ?></dc:date.Taken>
2945
   <description>
2946
    <?php print $thumb_html; ?> 
2947
   </description>
2948
   <pubDate><?php print strftime("%a, %d %b %Y %T %z", $details['time']); ?></pubDate>
2949
  </item>
2950
<?php
2951
2952
      }
2953
?>
2954
 </channel>
2955
</rss>
2956
<?php
2957
2958
2959
   } // getExport()
2960
2961
 
2962
   /**
2963
    * get all selected tags
2964
    *
2965
    * This function will return all selected tags as one string, seperated
2966
    * by a comma.
2967
    * @return array
2968
    */
2969
   private function getCurrentTags()
2970
   {
2971
      $current_tags = "";
2972
      if(isset($_SESSION['selected_tags']) && $_SESSION['selected_tags'] != "") {
2973
         foreach($_SESSION['selected_tags'] as $tag)
2974
            $current_tags.= $tag .",";
2975
         $current_tags = substr($current_tags, 0, strlen($current_tags)-1);
2976
      }
2977
      return $current_tags;
2978
2979
   } // getCurrentTags()
2980
2981
   /**
2982
    * return the current photo
2983
    */
2984
   public function get_current_photo()
2985
   {
2986
      if(isset($_SESSION['current_photo'])) {
2987
         return $_SESSION['current_photo'];
2988
      }
2989
2990
      return NULL;
2991
2992
   } // get_current_photo()
2993
2994
   /**
2995
    * current selected photo version
2996
    *
2997
    * this function returns the current selected photo version
2998
    * from the session variables.
2999
    *
3000
    * @return int
3001
    */
3002
   public function get_current_version()
3003
   {
3004
      /* if current version is set, return it, if the photo really has that version */
3005
      if(isset($_SESSION['current_version']) && is_numeric($_SESSION['current_version']))
3006
         return $_SESSION['current_version'];
3007
3008
      return false;
3009
3010
   } // get_current_version()
3011
3012
   /**
3013
    * returns latest available photo version
3014
    *
3015
    * this function returns the latested available version
3016
    * for the requested photo.
3017
    *
3018
    * @return int
3019
    */
3020
   public function get_latest_version($photo_idx)
3021
   {
3022
      /* try to get the lasted version for the current photo */
3023
      if($versions = $this->get_photo_versions($photo_idx))
3024
         return $versions[count($versions)-1];
3025
3026
      /* if no alternative version were found, return original version */
3027
      return 0;
3028
3029
   } // get_current_version()
3030
3031
   /**
3032
    * tells the client browser what to do
3033
    *
3034
    * this function is getting called via AJAX by the
3035
    * client browsers. it will tell them what they have
3036
    * to do next. This is necessary for directly jumping
3037
    * into photo index or single photo view when the are
3038
    * requested with specific URLs
3039
    * @return string
3040
    */
3041
   public function whatToDo()
3042
   {
3043
      if(isset($_SESSION['current_photo']) && $_SESSION['start_action'] == 'showp') {
3044
      }
3045
      elseif(isset($_SESSION['selected_tags']) && !empty($_SESSION['selected_tags'])) {
3046
         return "showpi_tags";
3047
      }
3048
      elseif(isset($_SESSION['start_action']) && $_SESSION['start_action'] == 'showpi') {
3049
         return "showpi";
3050
      }
3051
3052
   } // whatToDo()
3053
3054
   /**
3055
    * return the current process-user
3056
    * @return string
3057
    */
3058
   private function getuid()
3059
   {
3060
      if($uid = posix_getuid()) {
3061
         if($user = posix_getpwuid($uid)) {
3062
            return $user['name'];
3063
         }
3064
      }
3065
   
3066
      return 'n/a';
3067
   
3068
   } // getuid()
3069
3070
   /**
3071
    * photo version select list
3072
    *
3073
    * this function returns a HTML select list (drop down)
3074
    * to select a alternative photo version of the original photo.
3075
    *
3076
    * @param array $params
3077
    * @param smarty $smarty
3078
    * @return string
3079
    */
3080
   public function smarty_photo_version_select_list($params, &$smarty)
3081
   {
3082
      if(!isset($params['photo']) || !isset($params['current']))
3083
         return NULL;
3084
3085
      $output = "<option value=\"0\">Original</option>";
3086
      $versions = $this->get_photo_versions($params['photo']);
3087
3088
      foreach($versions as $version) {
3089
3090
         $output.= "<option value=\"". $version ."\"";
3091
         if($version == $params['current']) {
3092
            $output.= " selected=\"selected\"";
3093
         }
3094
         $output.= ">". $this->get_photo_version_name($params['photo'], $version) ."</option>";
3095
      }
3096
3097
      return $output;
3098
3099
   } // smarty_photo_version_select_list()
3100
3101
   /**
3102
    * returns a select-dropdown box to select photo index sort parameters
3103
    * @param array $params
3104
    * @param smarty $smarty
3105
    * @return string
3106
    */
3107
   public function smarty_sort_select_list($params, &$smarty)
3108
   {
3109
      $output = "";
3110
3111
      foreach($this->sort_orders as $key => $value) {
3112
         $output.= "<option value=\"". $key ."\"";
3113
         if($key == $_SESSION['sort_order']) {
3114
            $output.= " selected=\"selected\"";
3115
         }
3116
         $output.= ">". $value ."</option>";
3117
      }
3118
3119
      return $output;
3120
3121
   } // smarty_sort_select_list()
3122
3123
   /**
3124
    * returns the currently selected sort order
3125
    * @return string
3126
    */ 
3127
   private function get_sort_order()
3128
   {
3129
      switch($_SESSION['sort_order']) {
3130
         case 'date_asc':
3131
            return " ORDER BY p.time ASC";
3132
            break;
3133
         case 'date_desc':
3134
            return " ORDER BY p.time DESC";
3135
            break;
3136
         case 'name_asc':
3137
            if($this->dbver < 9) {
3138
               return " ORDER BY p.name ASC";
3139
            }
3140
            else {
3141
               return " ORDER BY basename(p.uri) ASC";
3142
            }
3143
            break;
3144
         case 'name_desc':
3145
            if($this->dbver < 9) {
3146
               return " ORDER BY p.name DESC";
3147
            }
3148
            else {
3149
               return " ORDER BY basename(p.uri) DESC";
3150
            }
3151
            break;
3152
         case 'tags_asc':
3153
            return " ORDER BY t.name ASC ,p.time ASC";
3154
            break;
3155
         case 'tags_desc':
3156
            return " ORDER BY t.name DESC ,p.time ASC";
3157
            break;
3158
         case 'rate_asc':
3159
            return " ORDER BY p.rating ASC, t.name ASC";
3160
            break;
3161
         case 'rate_desc':
3162
            return " ORDER BY p.rating DESC, t.name ASC";
3163
            break;
3164
      }
3165
3166
   } // get_sort_order()
3167
3168
   /**
3169
    * return the next to be shown slide show image
3170
    *
3171
    * this function returns the URL of the next image
3172
    * in the slideshow sequence.
3173
    * @return string
3174
    */
3175
   public function getNextSlideShowImage()
3176
   {
3177
      $all_photos = $this->getPhotoSelection();
3178
3179
      if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == count($all_photos)-1) 
3180
         $_SESSION['slideshow_img'] = 0;
3181
      else
3182
         $_SESSION['slideshow_img']++;
3183
3184
      if($this->is_user_friendly_url()) {
3185
         return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
3186
      }
3187
3188
      return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
3189
3190
   } // getNextSlideShowImage()
3191
3192
   /**
3193
    * return the previous to be shown slide show image
3194
    *
3195
    * this function returns the URL of the previous image
3196
    * in the slideshow sequence.
3197
    * @return string
3198
    */
3199
   public function getPrevSlideShowImage()
3200
   {
3201
      $all_photos = $this->getPhotoSelection();
3202
3203
      if(!isset($_SESSION['slideshow_img']) || $_SESSION['slideshow_img'] == 0)
3204
         $_SESSION['slideshow_img'] = 0;
3205
      else
3206
         $_SESSION['slideshow_img']--;
3207
3208
      if($this->is_user_friendly_url()) {
3209
         return $this->get_phpfspot_url() ."/photo/". $all_photos[$_SESSION['slideshow_img']] ."/". $this->cfg->photo_width;
3210
      }
3211
3212
      return $this->get_phpfspot_url() ."/phpfspot_img.php?idx=". $all_photos[$_SESSION['slideshow_img']] ."&width=". $this->cfg->photo_width;
3213
3214
   } // getPrevSlideShowImage()
3215
3216
   public function resetSlideShow()
3217
   {
3218
      if(isset($_SESSION['slideshow_img']))
3219
         unset($_SESSION['slideshow_img']);
3220
3221
   } // resetSlideShow()
3222
   
3223
   /**
3224
    * get random photo
3225
    *
3226
    * this function will get all photos from the fspot
3227
    * database and randomly return ONE entry
3228
    *
3229
    * saddly there is yet no sqlite3 function which returns
3230
    * the bulk result in array, so we have to fill up our
3231
    * own here.
3232
    * @return array
3233
    */
3234
   public function get_random_photo()
3235
   {
3236
      $all = Array();
3237
3238
      $query_str = "
3239
         SELECT p.id as id
3240
         FROM photos p
3241
      ";
3242
3243
      /* if show_tags is set, only return details for photos which
3244
         are specified to be shown
3245
      */
3246
      if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
3247
         $query_str.= "
3248
            INNER JOIN photo_tags pt
3249
               ON p.id=pt.photo_id
3250
            INNER JOIN tags t
3251
               ON pt.tag_id=t.id
3252
            WHERE
3253
               t.name IN ('".implode("','",$this->cfg->show_tags)."')";
3254
      }
3255
3256
      $result = $this->db->db_query($query_str);
3257
3258
      while($row = $this->db->db_fetch_object($result)) {
3259
         array_push($all, $row['id']);
3260
      }
3261
3262
      return $all[array_rand($all)];
3263
3264
   } // get_random_photo()
3265
3266
   /**
3267
    * get random photo tag photo
3268
    *
3269
    * this function will get all photos tagged with the requested
3270
    * tag from the fspot database and randomly return ONE entry
3271
    *
3272
    * saddly there is yet no sqlite3 function which returns
3273
    * the bulk result in array, so we have to fill up our
3274
    * own here.
3275
    * @return array
3276
    */
3277
   public function get_random_tag_photo($tagidx)
3278
   {
3279
      $all = Array();
3280
3281
      if(isset($this->cfg->show_tags) && !empty($this->cfg->show_tags)) {
3282
         $query_str.= "
3283
            SELECT
3284
               DISTINCT pt1.photo_id as id
3285
            FROM
3286
               photo_tags pt1
3287
            INNER JOIN photo_tags
3288
               pt2 ON pt1.photo_id=pt2.photo_id
3289
            INNER JOIN tags t1
3290
               ON t1.id=pt1.tag_id
3291
            INNER JOIN tags t2
3292
               ON t2.id=pt2.tag_id
3293
            WHERE
3294
               pt1.tag_id LIKE '". $tagidx ."'
3295
            AND
3296
               t2.name IN  ('".implode("','",$this->cfg->show_tags)."')
3297
            ORDER BY
3298
               t1.sort_priority ASC";
3299
      }
3300
      else {
3301
         $query_str = "
3302
            SELECT
3303
               p.id as id
3304
            FROM
3305
               photos p
3306
            INNER JOIN photo_tags pt
3307
               ON p.id=pt.photo_id
3308
            WHERE
3309
               pt.tag_id LIKE '". $tagidx ."'";
3310
      }
3311
3312
      $result = $this->db->db_query($query_str);
3313
3314
      while($row = $this->db->db_fetch_object($result)) {
3315
         array_push($all, $row['id']);
3316
      }
3317
3318
      return $all[array_rand($all)];
3319
3320
   } // get_random_tag_photo()
3321
3322
   /**
3323
    * validates provided date
3324
    *
3325
    * this function validates if the provided date
3326
    * contains a valid date and will return true 
3327
    * if it is.
3328
    * @param string $date_str
3329
    * @return boolean
3330
    */
3331
   public function isValidDate($date_str)
3332
   {
3333
      $timestamp = strtotime($date_str);
3334
   
3335
      if(is_numeric($timestamp))
3336
         return true;
3337
      
3338
      return false;
3339
3340
   } // isValidDate()
3341
3342
   /**
3343
    * timestamp to string conversion
3344
    * @param integer $timestamp
3345
    * @return string
3346
    */
3347
   private function ts2str($timestamp)
3348
   {
3349
      if(!empty($timestamp) && is_numeric($timestamp))
3350
         return strftime("%Y-%m-%d", $timestamp);
3351
3352
   } // ts2str()
3353
3354
   /**
3355
    * extract tag-names from $_GET['tags']
3356
    * @param string $tags_str
3357
    * @return string
3358
    */
3359
   private function extractTags($tags_str)
3360
   {
3361
      $not_validated = split(',', $tags_str);
3362
      $validated = array();
3363
3364
      foreach($not_validated as $tag) {
3365
         if(is_numeric($tag))
3366
            array_push($validated, $tag);
3367
      }
3368
   
3369
      return $validated;
3370
   
3371
   } // extractTags()
3372
3373
   /**
3374
    * returns the full path to a thumbnail
3375
    * @param integer $width
3376
    * @param integer $photo
3377
    * @return string
3378
    */
3379
   public function get_thumb_path($width, $photo_idx, $version_idx)
3380
   {
3381
      $md5 = $this->getMD5($photo_idx, $version_idx);
3382
      $sub_path = substr($md5, 0, 2);
3383
      return $this->cfg->thumb_path
3384
         . "/"
3385
         . $sub_path
3386
         . "/"
3387
         . $width
3388
         . "_"
3389
         . $md5;
3390
3391
   } // get_thumb_path()
3392
3393
   /**
3394
    * returns server's virtual host name
3395
    * @return string
3396
    */
3397
   private function get_server_name()
3398
   {
3399
      return $_SERVER['SERVER_NAME'];
3400
   } // get_server_name()
3401
3402
   /**
3403
    * returns type of webprotocol which is currently used
3404
    * @return string
3405
    */
3406
   private function get_web_protocol()
3407
   {
3408
      if(!isset($_SERVER['HTTPS']))
3409
         return "http";
3410
      else
3411
         return "https";
3412
   } // get_web_protocol()
3413
3414
   /**
3415
    * return url to this phpfspot installation
3416
    * @return string
3417
    */
3418
   private function get_phpfspot_url()
3419
   {
3420
      return $this->get_web_protocol() ."://". $this->get_server_name() . $this->cfg->web_path;
3421
3422
   } // get_phpfspot_url()
3423
3424
   /**
3425
    * returns the number of photos which are tagged with $tag_id
3426
    * @param integer $tag_id
3427
    * @return integer
3428
    */
3429
   public function get_num_photos($tag_id)
3430
   {
3431
      if($result = $this->db->db_fetchSingleRow("
3432
         SELECT count(*) as number
3433
         FROM photo_tags
3434
         WHERE
3435
            tag_id LIKE '". $tag_id ."'")) {
3436
3437
         return $result['number'];
3438
3439
      }
3440
3441
      return 0;
3442
      
3443
   } // get_num_photos()
3444
   
3445
   /**
3446
    * check file exists and is readable
3447
    *
3448
    * returns true, if everything is ok, otherwise false
3449
    * if $silent is not set, this function will output and
3450
    * error message
3451
    * @param string $file
3452
    * @param boolean $silent
3453
    * @return boolean
3454
    */
3455
   private function check_readable($file, $silent = null)
3456
   {
3457
      if(!file_exists($file)) {
3458
         if(!isset($silent))
3459
            print "File \"". $file ."\" does not exist.\n";
3460
         return false;
3461
      }
3462
3463
      if(!is_readable($file)) {
3464
         if(!isset($silent))
3465
            print "File \"". $file ."\" is not reachable for user ". $this->getuid() ."\n";
3466
         return false;
3467
      }
3468
3469
      return true;
3470
3471
   } // check_readable()
3472
3473
   /**
3474
    * check if all needed indices are present
3475
    *
3476
    * this function checks, if some needed indices are already
3477
    * present, or if not, create them on the fly. they are
3478
    * necessary to speed up some queries like that one look for
3479
    * all tags, when show_tags is specified in the configuration.
3480
    */
3481
   private function checkDbIndices()
3482
   {
3483
      $result = $this->db->db_exec("
3484
         CREATE INDEX IF NOT EXISTS
3485
            phototag
3486
         ON
3487
            photo_tags
3488
               (photo_id, tag_id)
3489
      ");
3490
3491
   } // checkDbIndices()
3492
3493
   /**
3494
    * retrive F-Spot database version
3495
    *
3496
    * this function will return the F-Spot database version number
3497
    * It is stored within the sqlite3 database in the table meta
3498
    * @return string|null
3499
    */
3500
   public function getFspotDBVersion()
3501
   {
3502
      if($result = $this->db->db_fetchSingleRow("
3503
         SELECT data as version
3504
         FROM meta
3505
         WHERE
3506
            name LIKE 'F-Spot Database Version'
3507
      "))
3508
         return $result['version'];
3509
3510
      return null;
3511
3512
   } // getFspotDBVersion()
3513
3514
   /**
3515
    * parse the provided URI and will returned the requested chunk
3516
    * @param string $uri
3517
    * @param string $mode
3518
    * @return string
3519
    */
3520
   public function parse_uri($uri, $mode)
3521
   {
3522
      if(($components = parse_url($uri)) === false)
3523
         return $uri;
3524
3525
      switch($mode) {
3526
         case 'filename':
3527
            return basename($components['path']);
3528
            break;
3529
         case 'dirname':
3530
            return dirname($components['path']);
3531
            break;
3532
         case 'fullpath':
3533
            return $components['path'];
3534
            break;
3535
         default:
3536
            $this->throwError("unknown mode ". $mode);
3537
            break;
3538
      }
3539
3540
   } // parse_uri()
3541
3542
   /**
3543
    * validate config options
3544
    *
3545
    * this function checks if all necessary configuration options are
3546
    * specified and set.
3547
    * @return boolean
3548
    */
3549
   private function check_config_options()
3550
   {
3551
      if(!isset($this->cfg->page_title) || $this->cfg->page_title == "")
3552
         $this->_error("Please set \$page_title in phpfspot_cfg");
3553
3554
      if(!isset($this->cfg->base_path) || $this->cfg->base_path == "")
3555
         $this->_error("Please set \$base_path in phpfspot_cfg");
3556
3557
      if(!isset($this->cfg->web_path) || $this->cfg->web_path == "")
3558
         $this->_error("Please set \$web_path in phpfspot_cfg");
3559
3560
      if(!isset($this->cfg->thumb_path) || $this->cfg->thumb_path == "")
3561
         $this->_error("Please set \$thumb_path in phpfspot_cfg");
3562
3563
      if(!isset($this->cfg->smarty_path) || $this->cfg->smarty_path == "")
3564
         $this->_error("Please set \$smarty_path in phpfspot_cfg");
3565
3566
      if(!isset($this->cfg->fspot_db) || $this->cfg->fspot_db == "")
3567
         $this->_error("Please set \$fspot_db in phpfspot_cfg");
3568
3569
      if(!isset($this->cfg->db_access) || $this->cfg->db_access == "")
3570
         $this->_error("Please set \$db_access in phpfspot_cfg");
3571
3572
      if(!isset($this->cfg->phpfspot_db) || $this->cfg->phpfspot_db == "")
3573
         $this->_error("Please set \$phpfspot_db in phpfspot_cfg");
3574
3575
      if(!isset($this->cfg->thumb_width) || $this->cfg->thumb_width == "")
3576
         $this->_error("Please set \$thumb_width in phpfspot_cfg");
3577
3578
      if(!isset($this->cfg->thumb_height) || $this->cfg->thumb_height == "")
3579
         $this->_error("Please set \$thumb_height in phpfspot_cfg");
3580
3581
      if(!isset($this->cfg->photo_width) || $this->cfg->photo_width == "")
3582
         $this->_error("Please set \$photo_width in phpfspot_cfg");
3583
3584
      if(!isset($this->cfg->mini_width) || $this->cfg->mini_width == "")
3585
         $this->_error("Please set \$mini_width in phpfspot_cfg");
3586
3587
      if(!isset($this->cfg->thumbs_per_page))
3588
         $this->_error("Please set \$thumbs_per_page in phpfspot_cfg");
3589
3590
      if(!isset($this->cfg->enable_replace_path))
3591
         $this->_error("Please set \$enable_replace_path in phpfspot_cfg");
3592
3593
      if($this->cfg->enable_replace_path == true) {
3594
         if(!isset($this->cfg->path_replace_from) || $this->cfg->path_replace_from == "")
3595
            $this->_error("Please set \$path_replace_from in phpfspot_cfg");
3596
3597
         if(!isset($this->cfg->path_replace_to) || $this->cfg->path_replace_to == "")
3598
            $this->_error("Please set \$path_replace_to in phpfspot_cfg");
3599
      }
3600
3601
      if(!isset($this->cfg->hide_tags))
3602
         $this->_error("Please set \$hide_tags in phpfspot_cfg");
3603
3604
      if(!isset($this->cfg->theme_name))
3605
         $this->_error("Please set \$theme_name in phpfspot_cfg");
3606
3607
      if(!isset($this->cfg->logging))
3608
         $this->_error("Please set \$logging in phpfspot_cfg");
3609
3610
      if(isset($this->cfg->logging) && $this->cfg->logging == 'logfile') {
3611
3612
         if(!isset($this->cfg->log_file))
3613
            $this->_error("Please set \$log_file because you set logging = log_file in phpfspot_cfg");
3614
3615
         if(!is_writeable($this->cfg->log_file))
3616
            $this->_error("The specified \$log_file ". $log_file ." is not writeable!");
3617
3618
      }
3619
3620
      /* remove trailing slash, if set */
3621
      if($this->cfg->web_path == "/")
3622
         $this->cfg->web_path = "";
3623
      elseif(preg_match('/\/$/', $this->cfg->web_path))
3624
         $this->cfg->web_path = preg_replace('/\/$/', '', $this->cfg->web_path);
3625
3626
      return $this->runtime_error;
3627
3628
   } // check_config_options()
3629
3630
   /**
3631
    * cleanup phpfspot own database
3632
    *
3633
    * When photos are getting delete from F-Spot, there will remain
3634
    * remain some residues in phpfspot own database. This function
3635
    * will try to wipe them out.
3636
    */
3637
   public function cleanup_phpfspot_db()
3638
   {
3639
      $to_delete = Array();
3640
3641
      $result = $this->cfg_db->db_query("
3642
         SELECT img_idx as img_idx
3643
         FROM images
3644
         ORDER BY img_idx ASC
3645
      ");
3646
3647
      while($row = $this->cfg_db->db_fetch_object($result)) {
3648
         if(!$this->db->db_fetchSingleRow("
3649
            SELECT id as id
3650
            FROM photos
3651
            WHERE id='". $row['img_idx'] ."'")) {
3652
3653
            array_push($to_delete, $row['img_idx'], ',');
3654
         }
3655
      }
3656
3657
      print count($to_delete) ." unnecessary objects will be removed from phpfspot's database.\n";
3658
3659
      $this->cfg_db->db_exec("
3660
         DELETE FROM images
3661
         WHERE img_idx IN (". implode($to_delete) .")
3662
      ");
3663
3664
   } // cleanup_phpfspot_db()
3665
3666
   /**
3667
    * return first image of the page, the $current photo
3668
    * lies in.
3669
    *
3670
    * this function is used to find out the first photo of the
3671
    * current page, in which the $current photo lies. this is
3672
    * used to display the correct photo, when calling showPhotoIndex()
3673
    * from showImage()
3674
    * @param integer $current
3675
    * @param integer $max
3676
    * @return integer
3677
    */
3678
   private function getCurrentPage($current, $max)
3679
   {
3680
      if(isset($this->cfg->thumbs_per_page) && !empty($this->cfg->thumbs_per_page)) {
3681
         for($page_start = 0; $page_start <= $max; $page_start+=$this->cfg->thumbs_per_page) {
3682
            if($current >= $page_start && $current < ($page_start+$this->cfg->thumbs_per_page))
3683
               return $page_start;
3684
         }
3685
      }
3686
      return 0;
3687
3688
   } // getCurrentPage()
3689
3690
   /**
3691
    * return mime info
3692
    *
3693
    * this function tries to find out the correct mime-type
3694
    * for the provided file.
3695
    * @param string $file
3696
    * @return string
3697
    */
3698
   public function get_mime_info($file)
3699
   {
3700
      $details = getimagesize($file);
3701
3702
      /* if getimagesize() returns empty, try at least to find out the
3703
         mime type.
3704
      */
3705
      if(empty($details) && function_exists('mime_content_type')) {
3706
3707
         // mime_content_type is marked as deprecated in the documentation,
3708
         // but is it really necessary to force users to install a PECL
3709
         // extension?
3710
         $details['mime'] = mime_content_type($file);
3711
      }
3712
3713
      return $details['mime'];
3714
3715
   } // get_mime_info()
3716
3717
   /**
3718
    * return tag-name by tag-idx
3719
    *
3720
    * this function returns the tag-name for the requested
3721
    * tag specified by tag-idx.
3722
    * @param integer $idx
3723
    * @return string
3724
    */
3725
   public function get_tag_name($idx)
3726
   {
3727
       if($result = $this->db->db_fetchSingleRow("
3728
         SELECT name as name
3729
         FROM tags
3730
         WHERE
3731
            id LIKE '". $idx ."'")) {
3732
3733
         return $result['name'];
3734
3735
      }
3736
3737
      return 0;
3738
      
3739
   } // get_tag_name()
3740
3741
   /**
3742
    * parse user friendly url which got rewritten by the websever
3743
    * @param string $request_uri
3744
    * @return string
3745
    */
3746
   private function parse_user_friendly_url($request_uri)
3747
   {
3748
      if(preg_match('/\/photoview\/|\/photo\/|\/tag\//', $request_uri)) {
3749
3750
         $options = explode('/', $request_uri);
3751
3752
         switch($options[1]) {
3753
            case 'photoview':
3754
               if(is_numeric($options[2])) {
3755
                  $this->session_cleanup();
3756
                  //unset($_SESSION['start_action']);
3757
                  //unset($_SESSION['selected_tags']);
3758
                  $_GET['mode'] = 'showp';
3759
                  return $this->showPhoto($options[2]);
3760
               }
3761
               break;
3762
            case 'photo':
3763
               if(is_numeric($options[2])) {
3764
                  $width = NULL;
3765
                  $version = NULL;
3766
                  if(isset($options[3]) && is_numeric($options[3]))
3767
                     $width = $options[3];
3768
                  if(isset($options[4]) && is_numeric($options[4]))
3769
                     $version = $options[4];
3770
                  require_once "phpfspot_img.php";
3771
                  $img = new PHPFSPOT_IMG;
3772
                  $img->showImg($options[2], $width, $version);
3773
               }
3774
               exit;
3775
               break;
3776
            case 'tag':
3777
               if(is_numeric($options[2])) {
3778
                  $this->session_cleanup();
3779
                  $_GET['tags'] = $options[2];
3780
                  $_SESSION['selected_tags'] = Array($options[2]);
3781
                  if(isset($options[3]) && is_numeric($options[3]))
3782
                     $_SESSION['begin_with'] = $options[3];
3783
                  return $this->showPhotoIndex();
3784
               }
3785
               break;
3786
         }
3787
      }
3788
3789
   } // parse_user_friendly_url()
3790
3791
   /**
3792
    * check if user-friendly-urls are enabled
3793
    *
3794
    * this function will return true, if the config option
3795
    * $user_friendly_url has been set. Otherwise false.
3796
    * @return boolean
3797
    */
3798
   private function is_user_friendly_url()
3799
   {
3800
      if(isset($this->cfg->user_friendly_url) && $this->cfg->user_friendly_url)
3801
         return true;
3802
3803
      return false;
3804
3805
   } // is_user_friendly_url()
3806
3807
   /**
3808
    * session cleanup
3809
    *
3810
    * this function will cleanup user's session information
3811
    */
3812
   private function session_cleanup()
3813
   {
3814
      unset($_SESSION['begin_with']);
3815
      $this->resetDateSearch();
3816
      $this->resetPhotoView();
3817
      $this->resetTagSearch();
3818
      $this->resetNameSearch();
3819
      $this->resetDateSearch();
3820
      $this->resetTags();
3821
3822
   } // session_cleanup()
3823
3824
   /**
3825
    * get database version
3826
    *
3827
    * this function queries the meta table
3828
    * and returns the current database version.
3829
    *
3830
    * @return integer
3831
    */
3832
   public function get_db_version()
3833
   {
3834
      if($row = $this->cfg_db->db_fetchSingleRow("
3835
         SELECT meta_value as meta_value
3836
         FROM
3837
            meta
3838
         WHERE
3839
            meta_key LIKE 'phpfspot Database Version'
3840
         ")) {
3841
3842
         return $row['meta_value'];
3843
3844
      }
3845
3846
      return 0;
3847
3848
   } // get_db_version()
3849
3850
   /**
3851
    * get photo versions
3852
    *
3853
    * this function returns an array of all available
3854
    * alterntaive versions of the provided photo id.
3855
    * has alternative photo versions available
3856
    *
3857
    * @param int $idx
3858
    * @return array
3859
    */
3860
   public function get_photo_versions($idx)
3861
   {
3862
      $versions = Array();
3863
3864
      $result = $this->db->db_query("
3865
         SELECT
3866
            version_id
3867
         FROM
3868
            photo_versions
3869
         WHERE
3870
            photo_id LIKE '". $idx ."'
3871
         ORDER BY
3872
            version_id ASC
3873
      ");
3874
3875
      while($row = $this->cfg_db->db_fetch_object($result)) {
3876
         array_push($versions, $row['version_id']);
3877
      }
3878
3879
      return $versions;
3880
3881
   } // get_photo_versions()
3882
3883
   /**
3884
    * check for invalid version of photo
3885
    *
3886
    * this function validates the provided photo-id and version-id
3887
    *
3888
    * @param int $photo_idx
3889
    * @param int $version_idx
3890
    * @return bool
3891
    */
3892
   public function is_valid_version($photo_idx, $version_idx)
3893
   {
3894
      /* the original version is always valid */
3895
      if($version_idx == 0)
3896
         return true;
3897
3898
      if($versions = $this->get_photo_versions($photo_idx)) {
3899
         if(in_array($version_idx, $versions))
3900
            return true;
3901
      }
3902
3903
      return false;
3904
3905
   } // is_valid_version()
3906
3907
   /**
3908
    * get photo version name
3909
    *
3910
    * this function returns the name of the version
3911
    * identified by the photo-id and version-id.
3912
    *
3913
    * @param int $photo_idx
3914
    * @param int $version_idx
3915
    * @return string
3916
    */
3917
   public function get_photo_version_name($photo_idx, $version_idx)
3918
   {
3919
      if($row = $this->db->db_fetchSingleRow("
3920
         SELECT
3921
            name
3922
         FROM
3923
            photo_versions
3924
         WHERE
3925
            photo_id LIKE '". $photo_idx ."'
3926
         AND
3927
            version_id LIKE '". $version_idx ."'")) {
3928
3929
         return $row['name'];
3930
3931
      }
3932
3933
      return false;
3934
3935
   } // get_photo_version_name()
3936
3937
   /**
3938
    */
3939
   public function is_valid_width($image_width)
3940
   {
3941
      if(in_array($image_width,
3942
         Array($this->cfg->thumb_width,
3943
               $this->cfg->photo_width,
3944
               $this->cfg->mini_width,
3945
               30)))
3946
3947
         return true;
3948
3949
      return false;
3950
3951
   } // is_valid_width()
3952
3953
} // class PHPFSPOT
3954
3955
?>
3956