Project

General

Profile

« Previous | Next » 

Revision 9f07f8ae

Added by Andreas Kohlbecker almost 3 years ago

ref #8028 support wor web-apps in media gallery
- refactoring procedural code to classes
- better differentiation of thumbnail-, full-size-image and web-app

View differences:

modules/cdm_dataportal/cdm_api/cdm_api.module
314 314
 * eu.etaxonomy.cdm.model.media.MediaUtils.filterAndOrderMediaRepresentations()
315 315
 *
316 316
 * @param object $media
317
 * @param array $mimeTypes
317
 * @param array $mime_types
318 318
 *    an array of mimetypes in their order of preference. e.g:
319 319
 *    array('application/pdf', 'image/png', 'image/jpeg', 'image/gif', 'text/html')
320 320
 * @param int $width
321 321
 *    The width of the optimal image. If null, the method will return the representation with the biggest expansion
322 322
 * @param int $height
323 323
 *    The height of the optimal image. If null, the method will return the representation with the biggest expansion
324
 *
324
 * @param bool $mime_types_filter_excludes
325
 *    Toggles the mode of the $mime_types filter to exclude when set to TRUE
326
 * @param bool $strict_size_limit
327
 *    Width an height parameters are treated as maximum values when this parameter is enabled.
325 328
 * @return array
326 329
 *   An array with preferred media representations or else an empty array.
327 330
 */
328
function cdm_preferred_media_representations($media, array $mimeTypes, $width = NULL, $height = NULL) {
329
  $prefRepr = array();
331
function cdm_preferred_media_representations($media, array $mime_types, $width = NULL, $height = NULL, $mime_types_filter_excludes = FALSE, $strict_size_limit = FALSE) {
332
  $matching_representations_by_rank = [];
330 333
  if (!isset($media->representations[0])) {
331
    return $prefRepr;
334
    return $matching_representations_by_rank;
332 335
  }
333 336

  
334
  while (count($mimeTypes) > 0) {
335
    // getRepresentationByMimeType
336
    $mimeType = array_shift($mimeTypes);
337

  
337 338

  
338 339
    foreach ($media->representations as &$representation) {
339 340
      // If the mimetype is not known, try inferring it.
......
342 343
          $representation->mimeType = infer_mime_type($representation->parts[0]->uri);
343 344
        }
344 345
      }
345

  
346
      if ($representation->mimeType == $mimeType) {
347
        // Preferred mimetype found -> erase all remaining mimetypes
348
        // to end loop.
349
        $mimeTypes = array();
350
        $expansionDeltaSum = 0;
346
      $mime_type_match = array_search($representation->mimeType, $mime_types);
347
      $match_per_mime_type_filter = $mime_types_filter_excludes ? !$mime_type_match : $mime_type_match;
348
      if ($match_per_mime_type_filter) {
349
        $expansion_delta_sum = 0;
351 350
        $valid_parts_cnt = 0;
352 351
        // Look for part with the best matching size.
353 352
        foreach ($representation->parts as $part) {
......
355 354
            // skip part if URI is missing
356 355
            continue;
357 356
          }
358
          $valid_parts_cnt++;
359
          $expansionDelta = PHP_INT_MAX; // biggest delta for unknown sizes
360

  
361
          // determine the optimal size
362
          if (isset($part->width) && isset($part->height)) {
363
            $expansion = $part->width * $part->height;
364
            if ($width != null && $height != null) {
365
              $optimalExpansion = $height * $width;
366
            } else {
367
              $optimalExpansion = PHP_INT_MAX;
357
          if($strict_size_limit){
358
            if (isset($part->width) && isset($part->height) && $part->width <= $width && $part->height <= $height) {
359
              $valid_parts_cnt++;
368 360
            }
369
            // determine the difference
370
            $expansionDelta = $expansion > $optimalExpansion ? $expansion - $optimalExpansion : $optimalExpansion - $expansion;
361
          } else {
362
            // calculate expansion delta
363
            $valid_parts_cnt++;
364
            $expansion_delta = PHP_INT_MAX; // biggest delta for unknown sizes
365

  
366
            // determine the optimal size
367
            if (isset($part->width) && isset($part->height)) {
368
              $expansion = $part->width * $part->height;
369
              if ($width != null && $height != null) {
370
                $optimalExpansion = $height * $width;
371
              } else {
372
                $optimalExpansion = PHP_INT_MAX;
373
              }
374
              // determine the difference
375
              $expansion_delta = $expansion > $optimalExpansion ? $expansion - $optimalExpansion : $optimalExpansion - $expansion;
376
            }
377
            // sum up the expansionDeltas of all parts contained in the representation
378
            $expansion_delta_sum += $expansion_delta;
371 379
          }
372
          // sum up the expansionDeltas of all parts contained in the representation
373
          $expansionDeltaSum += $expansionDelta;
374 380
        }
375 381
        if($valid_parts_cnt > 0){
376
          $expansionDeltaSum = $expansionDeltaSum / $valid_parts_cnt;
377
          $prefRepr[$expansionDeltaSum] = $representation;
382
          $expansion_delta_sum = $expansion_delta_sum / $valid_parts_cnt;
383
          $matching_representations_by_rank[$expansion_delta_sum] = $representation;
378 384
        }
379 385
      }
386

  
380 387
    }
381
  }
388

  
382 389
  // Sort the array so that the smallest key value is the first in the array
383
  ksort($prefRepr);
384
  return $prefRepr;
390
  ksort($matching_representations_by_rank);
391
  return $matching_representations_by_rank;
385 392
}
386 393

  
387 394
/**
......
811 818
      $obj = NULL;
812 819
    }
813 820

  
814
    $reponse_data = NULL;
821
    $response_data = NULL;
815 822

  
816 823
    if (function_exists('compose_' . $hook)){
817 824
      // call compose hook
818 825

  
819 826
      $elements =  call_user_func('compose_' . $hook, $obj);
820 827
      // pass the render array to drupal_render()
821
      $reponse_data = drupal_render($elements);
828
      $response_data = drupal_render($elements);
822 829
    } else {
823 830
      // call theme hook
824 831

  
......
833 840
            'tree_node_callback' => isset($args[2]) ? $args[2] : FALSE,
834 841
            'element_name'=> isset($args[3]) ? $args[3] : FALSE,
835 842
            );
836
          $reponse_data = theme($hook, $variables);
843
          $response_data = theme($hook, $variables);
837 844
          break;
838 845

  
839 846
        case 'cdm_list_of_taxa':
......
841 848
              'records' => $obj,
842 849
              'freetextSearchResults' => isset($args[0]) ? $args[0] : array(),
843 850
              'show_classification' => isset($args[1]) ? $args[1] : FALSE);
844
            $reponse_data = theme($hook, $variables);
851
            $response_data = theme($hook, $variables);
845 852
            break;
846 853

  
847
        case 'cdm_media_caption':
848
          $variables = $arg;
849
          $variables['media'] = $obj;
850

  
851
          $reponse_data = theme($hook, $variables);
852
          break;
853

  
854 854
        default:
855 855
          drupal_set_message(t(
856 856
          'Theme !theme is not yet supported by the function !function.', array(
......
863 863

  
864 864

  
865 865
    if($do_gzip){
866
      $reponse_data = gzencode($reponse_data, 2, FORCE_GZIP);
866
      $response_data = gzencode($response_data, 2, FORCE_GZIP);
867 867
      drupal_add_http_header('Content-Encoding', 'gzip');
868 868
    }
869 869
    drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
870
    drupal_add_http_header('Content-Length', strlen($reponse_data));
870
    drupal_add_http_header('Content-Length', strlen($response_data));
871 871

  
872
    print $reponse_data;
872
    print $response_data;
873 873
  } // END of handle $hook either as compose ot theme hook
874 874

  
875 875
}
modules/cdm_dataportal/cdm_dataportal.info
16 16
files[] = classes/AnnotationsAndSources.php
17 17
files[] = classes/NameRenderConfiguration.php
18 18
files[] = classes/DerivationTreeComposer.php
19
files[] = classes/GalleryItemMedia.php
20
files[] = classes/MediaSize.php
21
files[] = classes/ImageMediaItem.php
22
files[] = lib/FaviconDownloader.php
19 23

  
20 24
configure = admin/config/cdm_dataportal/settings
modules/cdm_dataportal/cdm_dataportal.module
34 34
  module_load_include('inc', 'cdm_dataportal', 'includes/media');
35 35
  module_load_include('inc', 'cdm_dataportal', 'includes/maps');
36 36
  module_load_include('inc', 'cdm_dataportal', 'includes/occurrences');
37
  module_load_include('inc', 'cdm_dataportal', 'includes/occurrences_new'); // tmp file, shoud be merged later with occurrences.inc
37
  module_load_include('inc', 'cdm_dataportal', 'includes/occurrences_new'); // tmp file, should be merged later with occurrences.inc
38 38
  module_load_include('inc', 'cdm_dataportal', 'includes/descriptions');
39 39
  module_load_include('inc', 'cdm_dataportal', 'includes/pre-drupal8');
40 40

  
......
51 51
  module_load_include('php', 'cdm_dataportal', 'classes/footnotekey');
52 52
  module_load_include('php', 'cdm_dataportal', 'classes/renderhints');
53 53

  
54

  
55 54
  /* ============================ java script functions ============================= */
56 55

  
57 56

  
modules/cdm_dataportal/classes/GalleryItemMedia.php
1
<?php
2

  
3

  
4
/**
5
 * Class GalleryItemMedia filters suitable MediaRepresentations from a CDM Media
6
 * object and prepares them for to be used as item in a MediaGallery.
7
 */
8
class GalleryItemMedia {
9

  
10
  public static $OK = 0;
11
  public static $ACCESS_DENIED = -1;
12
  public static $ERROR = -2;
13

  
14
  public static $image_mime_type_list_default =  [
15
    'image/jpg',
16
    'image/jpeg',
17
    'image/png',
18
    'image/gif',
19
  ];
20

  
21
  private $image_mime_type_list;
22
  private $thumbnail_width;
23
  private $thumbnail_height;
24

  
25
  private $thumbnail_representation = null;
26
  private $full_size_representation = null;
27
  private $web_app_representation = null;
28
  private $first_representation = null;
29

  
30
  /**
31
   * GalleryItemMedia constructor.
32
   */
33
  public function __construct($media, $thumbnail_width, $thumbnail_height, $image_mime_type_list = null) {
34
    if(!$image_mime_type_list){
35
      $this->image_mime_type_list  =  self::$image_mime_type_list_default;
36
    }
37
    $this->thumbnail_width = $thumbnail_width;
38
    $this->thumbnail_height =$thumbnail_height;
39
    $this->findRepresentations($media);
40
  }
41

  
42
  private function findRepresentations($media) {
43

  
44
    if (isset($media->representations[0]->parts[0])) {
45
      $thumbnail_representations = cdm_preferred_media_representations($media, $this->image_mime_type_list,
46
        $this->thumbnail_width, $this->thumbnail_height, FALSE, TRUE
47
      );
48
      $full_size_representations = cdm_preferred_media_representations($media, $this->image_mime_type_list);
49
      $web_app_representations = cdm_preferred_media_representations($media, $this->image_mime_type_list, NULL, NULL, TRUE);
50

  
51
      if (isset_not_empty($media->representations)) {
52
        // can be used as last resort fall back
53
        $this->first_representation = $media->representations[0];
54
      }
55

  
56
      if (isset_not_empty($this->thumbnail_representation)) {
57
        $this->thumbnail_representation = array_shift($thumbnail_representations);
58
      }
59
      if (isset_not_empty($full_size_representations)) {
60
        $this->full_size_representation = array_shift($full_size_representations);
61
      }
62
      if (isset_not_empty($web_app_representations)) {
63
        $this->web_app_representation = array_shift($web_app_representations);
64
      }
65
    }
66
  }
67

  
68
  public function getThumbnailImageMediaItem() {
69
    $url = $this->getThumbnailImageUrl();
70
    if($url){
71
      return new ImageMediaItem($this->getThumbnailImageUrl(), $this->getThumbnailSize());
72
    }
73
    return null;
74
  }
75

  
76
  /**
77
   * Provides the size of the thumbnail image if available.
78
   * In case this GalleryItemMedia contains as web app url the favicon
79
   * default size is returned.
80
   *
81
   * @return object|null
82
   */
83
  private function getThumbnailSize() {
84
    $repr = $this->getThumbnailRepresentation();
85
    if($repr){
86
      return $this->sizeOf($repr);
87
    } else  if($this->hasWebAppItem()) {
88
      // returning the favicon size, actual size is not important, in this case!
89
      return new MediaSize(64,64);
90
    }
91
    return null;
92
  }
93

  
94
  /**
95
   * The url of the thumbnail image if this is available. In case this
96
   * GalleryItemMedia contains as web app url the according favicon is returned
97
   * as thumbnail url.
98
   *
99
   * @return string|null
100
   *   The URL
101
   */
102
  private function getThumbnailImageUrl() {
103
    $repr = $this->getThumbnailRepresentation();
104
    if($repr){
105
      return $this->urlOf($repr);
106
    } else if($this->hasWebAppItem()) {
107
      return $this->webAppIcon($this->getWebAppUrl());
108
    }
109
    return null;
110
  }
111

  
112
  public function getWebAppUrl() {
113
    return $this->urlOf($this->web_app_representation);
114
  }
115

  
116
  public function hasWebAppItem(){
117
    return is_string($this->getWebAppUrl());
118
  }
119

  
120
  public function getFullSizeImageUrl() {
121
    if($this->full_size_representation){
122
      return $this->urlOf($this->full_size_representation);
123
    }
124
  }
125

  
126
  public function hasFullSizeImage(){
127
    return is_string($this->getFullSizeImageUrl());
128
  }
129

  
130
  /**
131
   * Provides the ULR to be used for showing the the item
132
   * in an overlay widget.
133
   *
134
   * This is currently limited to images only.
135
   *
136
   * @return string
137
   */
138
  public function getOverlayImageUrl(){
139
    if($this->hasFullSizeImage()){
140
      return $this->getFullSizeImageUrl();
141
    }
142
    return null;
143
  }
144

  
145
  /**
146
   * Provides the URL to be used for linking directly to the item.
147
   * Such links will open the item directly in the browser window.
148
   * WebApp urls are preferred over full-size-image urls.
149
   * The media item can be a web-app or image.
150
   *
151
   * @return mixed
152
   */
153
  public function getOpenItemUrl(){
154

  
155
    if($this->hasWebAppItem()){
156
      $url = $this->getWebAppUrl();
157
      if($this->urlIsAccessible($url) == self::$OK){
158
        return $url;
159
      }
160
    }
161
    if($this->hasFullSizeImage()){
162
      $url = $this->getFullSizeImageUrl();
163
      if($this->urlIsAccessible($url) == self::$OK){
164
        return $url;
165
      }
166
    }
167
    return null;
168
  }
169

  
170
  private function getThumbnailRepresentation() {
171
    if ($this->thumbnail_representation) {
172
      return $this->thumbnail_representation;
173
    } else {
174
      // use fallback strategy
175
      if ($this->full_size_representation) {
176
        return $this->full_size_representation;
177
      } else if(!$this->web_app_representation->uuid || $this->first_representation->uuid !== $this->web_app_representation->uuid){
178
        return $this->first_representation;
179
      }
180
    }
181
  }
182

  
183
  /**
184
   * @param $media_representation
185
   *  The CDM MediaRepresentation
186
   *
187
   * @return string|null
188
   *  The fist URL found in the representation parts or null
189
   */
190
  private function urlOf($media_representation){
191
    if(isset($media_representation->parts)){
192
      foreach($media_representation->parts as $part){
193
        if(isset($part->uri)){
194
          return $part->uri;
195
        }
196
      }
197
    }
198
    return null;
199
  }
200

  
201
  /**
202
   * @param $url
203
   *
204
   * @return false|mixed|string
205
   *
206
   * function found at https://stackoverflow.com/questions/5701593/how-to-get-a-websites-favicon-with-php#5702084
207
   */
208
  private function webAppIcon($url){
209
    $favicon = new FaviconDownloader($url);
210
    if ($favicon->icoExists) {
211
      return $favicon->icoUrl;
212
    }
213
    return null;
214
  }
215

  
216
  /**
217
   * @param $url
218
   *
219
   * @return int
220
   *  One of the GalleryItemMedia constants: $OK, $ACCESS_DENIED, $ERROR
221
   */
222
  private function urlIsAccessible($url){
223
    $result = drupal_http_request($url, ['method' => 'HEAD']);
224
    if($result->code == 200){
225
      return GalleryItemMedia::$OK;
226
    } else if($result->code >= 401 && $result->code <= 403) {
227
      return GalleryItemMedia::$ACCESS_DENIED;
228
    } else {
229
      return GalleryItemMedia::$ERROR;
230
    }
231
  }
232

  
233
  /**
234
   * @param $media_representation
235
   *
236
   * @return MediaSize object
237
   *    A MediaSize object with the fields
238
   *    - width: may be unset
239
   *    - height: may be unset
240
   *   or null if no size information is available
241
   */
242
  private function sizeOf($media_representation){
243
    if(isset($media_representation->parts[0])){
244
      $part = $media_representation->parts[0];
245
      if (isset_not_empty($part->width) || isset_not_empty($part->height)) {
246
        return new MediaSize(
247
          @$media_representation->parts[0]->width,
248
          @$media_representation->parts[0]->height
249
        );
250
      }
251
    }
252
    return null;
253
  }
254

  
255

  
256
}
modules/cdm_dataportal/classes/ImageMediaItem.php
1
<?php
2

  
3

  
4
class ImageMediaItem {
5

  
6
  private $size;
7
  private $url;
8

  
9
  /**
10
   * ImageMediaItem constructor.
11
   */
12
  public function __construct($url, MediaSize $size = null) {
13
    $this->size = $size;
14
    $this->url = $url;
15
  }
16

  
17
  public static function fromMediaRepresentationPart($mediaRepresentationPart){
18
    $h = @$mediaRepresentationPart->height;
19
    $w = @$mediaRepresentationPart->width;
20
    return new ImageMediaItem($mediaRepresentationPart->url, new MediaSize($w, $h));
21
  }
22

  
23
  /**
24
   * @return \MediaSize
25
   */
26
  public function getSize() {
27
    return $this->size;
28
  }
29

  
30
  /**
31
   * @return mixed
32
   */
33
  public function getUrl() {
34
    return $this->url;
35
  }
36

  
37
}
modules/cdm_dataportal/classes/MediaSize.php
1
<?php
2

  
3

  
4
class MediaSize {
5

  
6
  public $width, $height;
7

  
8
  /**
9
   * MediaSize constructor.
10
   */
11
  public function __construct($width, $height) {
12
    $this->width = $width;
13
    $this->height = $height;
14
  }
15

  
16
}
modules/cdm_dataportal/includes/descriptions.inc
681 681
    $captionElements = array('title', 'rights');
682 682

  
683 683
    if (isset($media_list[0]) && isset($gallery_settings['cdm_dataportal_media_maxextend']) && isset($gallery_settings['cdm_dataportal_media_cols'])) {
684
      $gallery = compose_cdm_media_gallerie(array(
684
      $gallery = compose_cdm_media_gallery(array(
685 685
        'mediaList' => $media_list,
686 686
        'galleryName' => CDM_DATAPORTAL_DESCRIPTION_GALLERY_NAME . '_' . $feature_node->term->uuid,
687 687
        'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
......
858 858
 *
859 859
 * @return string
860 860
 *    The markup
861
 * 
861
 *
862 862
 * @ingroup compose
863 863
 */
864 864
function cdm_description_element_media($descriptionElement, $mimeTypePreference) {
modules/cdm_dataportal/includes/media.inc
254 254
 *   A render array for the image gallery
255 255
 * @ingroup: compose
256 256
 */
257
function compose_cdm_media_gallerie($configuration) {
257
function compose_cdm_media_gallery($configuration) {
258 258

  
259 259
  $mediaList = $configuration['mediaList'];
260 260

  
......
269 269
    array(
270 270
      'mediaList' => NULL,
271 271
      'galleryName' => NULL,
272
      'maxExtend' => 150,
272
      'max_extend' => 150,
273 273
      'cols' => 4,
274 274
      'maxRows' => FALSE,
275 275
      'captionElements' => array('title'),
276 276
      'mediaLinkType' => 'LIGHTBOX',
277
      'alternativeMediaUri' => NULL,
277
      'alternative_media_uri' => NULL,
278 278
      'galleryLinkUri' => NULL,
279 279
      'showCaption' => TRUE,
280 280
    ),
281 281
    $configuration);
282 282

  
283
    $image_mime_type_list = [
284
      'image/jpg',
285
      'image/jpeg',
286
      'image/png',
287
      'image/gif',
288
    ];
289

  
283 290
  $galleryName = $configuration['galleryName'];
284
  $maxExtend = $configuration['maxExtend'];
291
  $max_extend = $configuration['max_extend'];
285 292
  $cols = $configuration['cols'];
286 293
  $maxRows = $configuration['maxRows'];
287 294
  $captionElements = $configuration['captionElements'];
288 295
  $mediaLinkType = $configuration['mediaLinkType'];
289
  $alternativeMediaUri = $configuration['alternativeMediaUri'];
296
  $alternative_media_uri = $configuration['alternative_media_uri'];
290 297
  $galleryLinkUri = $configuration['galleryLinkUri'];
291 298
  $showCaption = $configuration['showCaption'];
292 299

  
293
  $caption_link_uri = '';
300
  $do_link_to_media = '';
294 301
  if(isset($captionElements['#uri'])){
295
    $caption_link_uri = $captionElements['#uri'];
302
    $do_link_to_media = $captionElements['#uri'];
296 303
    unset($captionElements['#uri']);
297 304
  }
298 305
  if (!is_array($captionElements) || count($captionElements) == 0) {
......
354 361
      $complete_media = cdm_ws_get(CDM_WS_MEDIA, $media->uuid);
355 362
      if (isset($complete_media->representations[0]->parts[0])) {
356 363

  
357
          //
358
          // Find preferred representation.
359
          //
360

  
361
          $thumbnail_representations = cdm_preferred_media_representations($complete_media, array(
362
              'image/jpg',
363
              'image/jpeg',
364
              'image/png',
365
              'image/gif',
366
          ),
367
              $maxExtend,
368
              $maxExtend
369
          );
370
          // due to a bug the portal/taxon/{uuid}/media service only delivers a filtered media object
371
          // which only contains the thumbnail representation even if the height and width filters are not set.
372
          // --> #6970
373
          // to get hold of the full resolution images we send a separate request :
374
          // $complete_media = cdm_ws_get(CDM_WS_MEDIA, $media->uuid);
375
          $full_size_representations = cdm_preferred_media_representations($complete_media, array(
376
                  'image/jpg',
377
                  'image/jpeg',
378
                  'image/png',
379
                  'image/gif',
380
              )
381
          );
382
          if (count($thumbnail_representations) == 0) {
383
              // Fallback to using the first one in the list.
384
              $thumbnail_representations = $media->representations;
385
              $full_size_representations = $media->representations;
386
          }
387
          $thumbnail_representation = array_shift($thumbnail_representations);
388
          $full_size_representation = array_shift($full_size_representations);
389

  
390
          // $preferred_media_representation->parts[0]->uri =
391
          // "http://127.0.0.1/images/palmae/palm_tc_14415_1.jpg";
392
          $contentTypeDirectory = media_content_type_dir($thumbnail_representation, 'application');
393

  
394
          switch($contentTypeDirectory) {
395
            case 'image':
396
              $mediaPartHtml = cdm_media_gallerie_entry_image($thumbnail_representation->parts[0], $maxExtend, TRUE);
397
              break;
398
            case 'video':
399
            case 'audio':
400
            case 'video':
401
            case 'application':
402
            case 'text':
403
            default:
404
              $mediaPartHtml = cdm_media_gallerie_entry_webapp($thumbnail_representation->parts[0], $maxExtend, TRUE);
405
          }
406
          // --- Compose Media Link.
407
          $mediaLinkUri = FALSE;
408
          if ($alternativeMediaUri) {
409
              if (isset($alternativeMediaUri[$mediaIndex])) {
410
                  $mediaLinkUri = $alternativeMediaUri[$mediaIndex];
411
              }
412
              if (is_string($alternativeMediaUri)) {
413
                  $mediaLinkUri = $alternativeMediaUri;
414
              }
415
          } else {
416
              $mediaLinkUri = $full_size_representation->parts[0]->uri;
417
          }
418
          $mediaIndex++;
419

  
420
          // media captions will be loaded via AHAH
421
          _add_js_ahah();
422
          $content_url = cdm_compose_ws_url(CDM_WS_PORTAL_MEDIA, $media->uuid);
423
          //if $captionElements == null, set $captionPartHtml = '' otherwise the loading image is shown but nothing is loaded.
424
          if ($captionElements) {
425

  
426
              $cdm_proxy_url_caption = url('cdm_api/proxy/' . urlencode($content_url) . "/cdm_media_caption/" . serialize($captionElements));
427
              $ahah_media_caption = '<div class="ahah-content" data-cdm-ahah-url="' . $cdm_proxy_url_caption . '">'
428
                  . '<span class="loading" style="display: none;">' . loading_image_html() . '</span></div>';
429

  
430
            // preparing the part link (= click on image itself) which can be handled in two ways
431
            //
432
            //  1. open image in lightbox, the captions in the lightbox will be loaded via AHAH
433
            //  2. open the media in a new window with target 'specimen'
434
            if ($mediaLinkType == 'LIGHTBOX' && $contentTypeDirectory == 'image') {
435
              $mediaPartLinkAttributes['class'] = array('lightbox');
436
            } else {
437
              $mediaPartLinkAttributes['target'] = "specimen";
438
              $openMediaLinkAttributes['target'] = "specimen";
364
        $gallery_media_item = new GalleryItemMedia($complete_media, $max_extend, $max_extend);
365

  
366
//        if($gallery_media_item->hasWebAppItem()) {
367
//            $media_part_markup = cdm_media_gallerie_entry_webapp($gallery_media_item, $max_extend, TRUE);
368
//        } else {
369
        $thumbnail_item = $gallery_media_item->getThumbnailImageMediaItem();
370
            $media_part_markup = cdm_media_gallery_entry_image($thumbnail_item, $max_extend, TRUE);
371
//        }
372

  
373
        // --- Compose Media Link.
374
        $media_link_uri = FALSE;
375
        if ($alternative_media_uri) {
376
            if (isset($alternative_media_uri[$mediaIndex])) {
377
                $media_link_uri = $alternative_media_uri[$mediaIndex];
439 378
            }
440
            $mediaPartLinkAttributes['alt'] = htmlentities($ahah_media_caption);
441

  
442
            // --- preparing the media caption
443

  
444
            /* old comment: "no caption elements to show up here except the $caption_link_uri, if at all"
445
             *
446
             * a.kohlbecker 2013-03-14 :
447
             *   It is unclear why no caption elements should be shown, Was it a technical reason?
448
             *   see commit r16723 740177eb-a1d8-4ec3-a630-accd905eb3da
449
             *   If not problems arise with this remove it after some weeks
450
             */
451
            $captionPartHtml = $ahah_media_caption;
452
          }else{
453
              $captionPartHtml = '';
379
            if (is_string($alternative_media_uri)) {
380
                $media_link_uri = $alternative_media_uri;
381
            }
382
        } else {
383
            $media_link_uri = $gallery_media_item->getOpenItemUrl();
384
        }
385
        $mediaIndex++;
386

  
387
        // media captions will be loaded via AHAH
388
        _add_js_ahah();
389
        $content_url = cdm_compose_ws_url(CDM_WS_PORTAL_MEDIA, $media->uuid);
390
        //if $captionElements == null, set $caption_part_markup = '' otherwise the loading image is shown but nothing is loaded.
391
        if ($captionElements) {
392
          $cdm_proxy_url_caption = url('cdm_api/proxy/' . urlencode($content_url) . "/cdm_media_caption/" . serialize($captionElements));
393
          $ahah_media_caption = '<div class="ahah-content" data-cdm-ahah-url="' . $cdm_proxy_url_caption . '">'
394
              . '<span class="loading" style="display: none;">' . loading_image_html() . '</span></div>';
395

  
396
        // preparing the part link (= click on image itself) which can be handled in two ways
397
        //
398
        //  1. open image in lightbox, the captions in the lightbox will be loaded via AHAH
399
        //  2. open the media in a new window with target 'specimen'
400
        if ($mediaLinkType == 'LIGHTBOX' && $gallery_media_item->hasFullSizeImage()) {
401
          $mediaPartLinkAttributes['class'] = array('lightbox');
402
        } else {
403
          $mediaPartLinkAttributes['target'] = "specimen";
404
          $openMediaLinkAttributes['target'] = "specimen";
405
        }
406
        $mediaPartLinkAttributes['alt'] = htmlentities($ahah_media_caption);
407

  
408
        // --- preparing the media caption
409

  
410
        /* old comment: "no caption elements to show up here except the $caption_link_uri, if at all"
411
         *
412
         * a.kohlbecker 2013-03-14 :
413
         *   It is unclear why no caption elements should be shown, Was it a technical reason?
414
         *   see commit r16723 740177eb-a1d8-4ec3-a630-accd905eb3da
415
         *   If not problems arise with this remove it after some weeks
416
         */
417
        $caption_part_markup = $ahah_media_caption;
418
      }else{
419
          // no caption_elements --> empty caption
420
          $caption_part_markup = '';
421
      }
422
        if ($do_link_to_media) {
423
          if ($gallery_media_item->hasFullSizeImage()) {
424
            // it is an image, so open it in the media page
425
            $caption_part_markup .= '<div class="media-caption-link">' . l("Open in viewer " . custom_icon_font_markup('icon-interal-link-alt-solid'), path_to_media($media->uuid), array(
426
                'attributes' => array(), 'html' => TRUE,
427
                    )) . '</div>';
454 428
          }
455
          if ($caption_link_uri) {
456
              if ($contentTypeDirectory == 'image') {
457
                // it is an image, so open it in the media page
458

  
459
                $captionPartHtml .= '<div class="media-caption-link">' . l($caption_link_uri, path_to_media($media->uuid), array(
460
                    'attributes' => array(), 'html' => TRUE,
461
                        )) . '</div>';
462

  
463

  
464
              }
465
              else {
466
                // otherwise open it directly and let the the browser handle the media type
467
                $openMediaLinkAttributes['absolute'] = TRUE;
468
                $captionPartHtml .= '<div class="media-caption-link">' . l($caption_link_uri, $mediaLinkUri, array(
469
                    'attributes' => $openMediaLinkAttributes, 'html' => TRUE,
470
                  )) . '</div>';
471
              }
429
          if ($gallery_media_item->hasWebAppItem()) {
430
            // otherwise open it directly and let the the browser handle the media type
431
            $openMediaLinkAttributes['absolute'] = TRUE;
432
            $caption_part_markup .= '<div class="media-caption-link">' . l("Open external page " . font_awesome_icon_markup('fa-external-link-alt', array('class' => array('superscript'))), $gallery_media_item->getWebAppUrl(), array(
433
                'attributes' => $openMediaLinkAttributes, 'html' => TRUE,
434
              )) . '</div>';
472 435
          }
436
        }
473 437

  
474
          $captionParts[] = $captionPartHtml;
438
      $captionParts[] = $caption_part_markup;
475 439

  
476
            // --- Surround imagePart with link, this .
477
          if ($doLink) {
478
            $mediaPartHtml = l($mediaPartHtml, $mediaLinkUri, array(
479
                'attributes' => $mediaPartLinkAttributes, 'html' => TRUE,
480
            ));
481
          }
482
      }
483
      else {
484
        $mediaPartHtml = '';
440
      // --- Surround imagePart with link, this .
441
        if ($doLink) {
442
          $media_part_markup = l($media_part_markup, $gallery_media_item->getOverlayImageUrl(), array(
443
              'attributes' => $mediaPartLinkAttributes, 'html' => TRUE,
444
          ));
445
        }
446
      } else {
447
        $media_part_markup = '';
485 448
        $captionParts[] = '';
486 449
      }
487
      $out .= '<td class="media">' . $mediaPartHtml . '</td>';
450
      $out .= '<td class="media">' . $media_part_markup . '</td>';
488 451
    }
489 452
    $out .= '</tr>'; // End of media parts.
490 453
    if ($showCaption) {
491
      if ( (is_array($captionElements) && count($captionElements) > 0) || $caption_link_uri) {
454
      if ( (is_array($captionElements) && count($captionElements) > 0) || $do_link_to_media) {
492 455
        $out .= '<tr class="caption-row">';
493 456
        // Add caption row.
494
        foreach ($captionParts as $captionPartHtml) {
495
          $out .= '<td class="caption">' . $captionPartHtml . '</td>';
457
        foreach ($captionParts as $caption_part_markup) {
458
          $out .= '<td class="caption">' . $caption_part_markup . '</td>';
496 459
        }
497 460
        $out .= '</tr>';
498 461
      }
......
517 480
/**
518 481
 * Creates markup for a CDM MediaRepresentation which is referencing an image.
519 482
 *
520
 * @param $mediaRepresentationPart
483
 * @param \ImageMediaItem $thumbnailImage
521 484
 * @param $maxExtend
522 485
 * @param $addPassePartout
523 486
 * @param $attributes
524 487
 *   An array of attributes for the img tag.
488
 *
525 489
 * @return string
526 490
 *   The markup for the media representation
527 491
 */
528
function cdm_media_gallerie_entry_image($mediaRepresentationPart, $maxExtend, $addPassePartout, $attributes = array()) {
492
function cdm_media_gallery_entry_image($thumbnailImage, $maxExtend, $addPassePartout, $attributes = array()) {
529 493

  
530 494
  $out = '';
531 495

  
532 496
  // TODO merge with theme_cdm_media_mime_image?
533
  if (isset($mediaRepresentationPart)) {
497
  if (isset($thumbnailImage)) {
534 498

  
535
    $h = @$mediaRepresentationPart->height;
536
    $w = @$mediaRepresentationPart->width;
499
    $size = $thumbnailImage->getSize();
500
    $h = @$size->height;
501
    $w = @$size->width;
502
    $thumbnail_image_uri = $thumbnailImage->getUrl();
537 503
    if (!($w && $h)) {
538 504
      // Take url and replace spaces.
539
      $image_uri = str_replace(' ', '%20', $mediaRepresentationPart->uri);
540
      $imageDimensions = getimagesize_remote($image_uri);
505
      // TODO this should go into the ImageMediaItem
506
      $imageDimensions = getimagesize_remote(str_replace(' ', '%20', $thumbnail_image_uri));
541 507
      if (!$imageDimensions) {
542
        return '<div>' . t('Image unavailable, uri: ') . $mediaRepresentationPart->uri . '</div>';
508
        return '<div>' . t('Image unavailable, uri: ') . $thumbnail_image_uri . '</div>';
543 509
      }
544 510
      $w = $imageDimensions[0];
545 511
      $h = $imageDimensions[1];
......
564 530
      $attributes = array();
565 531
    }
566 532
    if(!isset($attributes['alt'])){
567
      $attributes['alt'] = check_plain($mediaRepresentationPart->uri);
533
      $attributes['alt'] = check_plain($thumbnail_image_uri);
568 534
    }
569 535
    $attrStr = ' ';
570 536
    // $attributes['title'] = 'h:'.$h.', w:'.$w.',ratio:'.$ratio;
......
576 542

  
577 543
    if ($addPassePartout) {
578 544
      $out .= '<div class="image-passe-partout" style="width:' . $maxExtend . 'px; height:' . $maxExtend . 'px;">';
579
    }
580
    else {
581
      // Do not add margins if no pass partout is shown.
545
    } else {
546
      // Do not add margins if no pass-partout is shown.
582 547
      $margins = '';
583 548
    }
584
    $out .= '<img src="' . $mediaRepresentationPart->uri . '" width="' . $displayWidth . '" height="' . $displayHeight . '" style="' . $margins . '"' . $attrStr . ' />';
585

  
549
    $out .= '<img src="' . $thumbnail_image_uri . '" width="' . $displayWidth . '" height="' . $displayHeight . '" style="' . $margins . '"' . $attrStr . ' />';
586 550
    if ($addPassePartout) {
587 551
      $out .= '</div>';
588 552
    }
......
594 558
/**
595 559
 * Creates markup for a CDM MediaRepresentation which is referencing an web application.
596 560
 *
597
 * @param $mediaRepresentationPart
561
 * @param ImageMediaItem $galleryItemMedia
598 562
 * @param $maxExtend
599 563
 * @param $addPassePartout
600 564
 * @param $attributes
565
 *
601 566
 * @return string
602 567
 *   The markup for the media representation
603 568
 */
604
function cdm_media_gallerie_entry_webapp($mediaRepresentationPart, $maxExtend, $addPassePartout, $attributes = array()) {
569
function cdm_media_gallerie_entry_webapp($galleryItemMedia, $maxExtend, $addPassePartout, $attributes = array()) {
605 570

  
606 571
  $out = '';
607 572

  
608
  if (isset($mediaRepresentationPart)) {
609
    $out = '<div class="application">Link to external media resource or web application</div>';
573
  if (isset($galleryItemMedia)) {
574
    $out = '<div class="application"><image src="' . $galleryItemMedia->getUrl() . '"></div>';
610 575
    if ($addPassePartout) {
611 576
      $out .= '<div class="image-passe-partout" style="width:' . $maxExtend . 'px; height:' . $maxExtend . 'px;">' . $out .'</div>';
612 577
    }
......
760 725
    $gallery_settings = getGallerySettings(CDM_DATAPORTAL_TAXON_MEDIA_GALLERY_NAME_TAB);
761 726

  
762 727
    $out = '<div class="image-gallerie">';
763
    $out .= compose_cdm_media_gallerie(array(
728
    $out .= compose_cdm_media_gallery(array(
764 729
      'mediaList' => $media,
765 730
      'galleryName' => $gallery_name,
766 731
      'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
......
866 831
}
867 832

  
868 833
/**
869
 * @todo Please document this function.
870
 * @see http://drupal.org/node/1354
834
 * Extracts the content type directory from the mime type of the passed
835
 * CDM MediaRepresentation.
871 836
 */
872 837
function media_content_type_dir($media_representation, $default = FALSE) {
873
  if ($media_representation->mimeType) {
874
    return substr($media_representation->mimeType, 0, stripos($media_representation->mimeType, '/'));
875
  }
876
  else {
838
  if (isset_not_empty($media_representation->mimeType)) {
839
    $ctd = substr($media_representation->mimeType, 0, stripos($media_representation->mimeType, '/'));
840
    return $ctd;
841
  } else {
877 842
    return $default;
878 843
  }
879 844
}
......
987 952
 *         example:
988 953
 *          Show 'title', 'description', 'file', 'filename' in the caption:
989 954
 *          array('title', 'description', 'file', 'filename')
990
 * @return string
991
 *   the markup
955
 * @return array
956
 *   A drupal render array
992 957
 *
993 958
 * @ingroup compose
994 959
 */
......
1007 972
  $doRights = !$elements || array_search('rights', $elements) !== FALSE;
1008 973

  
1009 974
  $descriptionPrefix = "";
1010
  $footnote_list_key = 'media-' . $media->uuid;
975
  $footnote_list_key = cdm_media_foot_note_key($media);
1011 976

  
1012 977
  // handle sources and annotations
1013 978
  cdm_lazyload_array_field('media', 'annotations', $media);
......
1022 987
    $media, NULL, $footnote_list_key
1023 988
  );
1024 989

  
1025
  $out = '';
990
  $render_array = [];
991

  
1026 992

  
1027 993
  // Title.
1028 994
  if ($doTitle) {
......
1033 999
      // Use filename as fallback option if no description and no source citations are available.
1034 1000
      $title_string = $media_metadata['filename'];
1035 1001
    }
1036
    $out .= '<div class="title">' . $title_string . '</div>';
1002
    $render_array['title'] = markup_to_render_array('<div class="title">' . $title_string . '</div>');
1037 1003
  }
1038 1004

  
1039 1005
  // Description.
1040 1006
  if ($media_metadata['description'] && $doDescription) {
1041
    $out .= '<p class="description">' . $media_metadata['description'] . '</p>';
1007
    $render_array['description'] = markup_to_render_array('<p class="description">' . $media_metadata['description'] . '</p>');
1042 1008
  }
1043 1009

  
1044
  $groups = array();
1010
  //  $render_array['dl']  = markup_to_render_array('<dl class="media-caption">');
1045 1011

  
1046
  $out .= '<dl class="media-caption">';
1012
  $groups = array();
1047 1013
  // Artist.
1048 1014
  if ($media_metadata['artist'] && $doArtist) {
1049 1015
    _description_list_group_add($groups, t('Artist') . ':', $media_metadata['artist'] );
......
1086 1052

  
1087 1053
  // TODO add all other metadata elements generically.
1088 1054

  
1089
  $description_list_item = array(
1055
  $description_list = array(
1090 1056
    '#theme' => 'description_list',
1091 1057
    '#groups' => $groups,
1092 1058
    '#attributes' => array('class' => 'media-caption')
1093 1059
  );
1094
  $out .= drupal_render($description_list_item);
1095
  $out .= render_footnotes($footnote_list_key);
1096
  return $out;
1060
  $render_array['description_list'] = $description_list;
1061
  $render_array['footnotes'] = markup_to_render_array(render_footnotes(cdm_media_foot_note_key($media)));
1062

  
1063
  return $render_array;
1064
}
1065

  
1066
/**
1067
 * @param $media
1068
 *
1069
 * @return string
1070
 */
1071
function cdm_media_foot_note_key($media) {
1072
  return 'media-' . $media->uuid;
1097 1073
}
1074

  
modules/cdm_dataportal/includes/name.inc
1326 1326
            'elements' => array('-none-'),
1327 1327
            'sources_as_content' => true
1328 1328
          );
1329
          $media = compose_cdm_media_gallerie(array(
1329
          $media = compose_cdm_media_gallery(array(
1330 1330
            'mediaList' => array($mediaSpecimen),
1331 1331
            'galleryName' => CDM_DATAPORTAL_TYPE_SPECIMEN_GALLERY_NAME . '_' . $type_designation_entity_ref->uuid,
1332 1332
            'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
modules/cdm_dataportal/includes/occurrences.inc
193 193

  
194 194

  
195 195

  
196
        $gallery_html = compose_cdm_media_gallerie(array(
196
        $gallery_html = compose_cdm_media_gallery(array(
197 197
            'mediaList' => $images,
198 198
            'galleryName' => $galleryName,
199 199
            'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
......
482 482
                            $captionElements = array(
483 483
                                '#uri' => t('open media'),
484 484
                            );
485
                            $gallery_html = compose_cdm_media_gallerie(array(
485
                            $gallery_html = compose_cdm_media_gallery(array(
486 486
                                'mediaList' => $value, // WARNING: this is either a List<Media> or List<MediaDTO>, see above ~line 361
487 487
                                'galleryName' => $specimen_or_observation->titleCache,
488 488
                                'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
......
887 887
                case 'chromatograms':
888 888
                    @_description_list_group_add($groups, cdm_occurrence_field_name_label($field),
889 889
                        array(
890
                            '#markup' => compose_cdm_media_gallerie(array('medialist' => $value)),
890
                            '#markup' => compose_cdm_media_gallery(array('medialist' => $value)),
891 891
                        ),
892 892
                        NULL,
893 893
                        11);
......
1255 1255
                        'title',
1256 1256
                        '#uri' => t('open media'),
1257 1257
                    );
1258
                    $gallery_html = compose_cdm_media_gallerie(array(
1258
                    $gallery_html = compose_cdm_media_gallery(array(
1259 1259
                        'mediaList' => $value,
1260 1260
                        'galleryName' => $specimen_or_observation_dto->uuid,
1261 1261
                        'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
......
1562 1562
          '#uri' => t('open media'),
1563 1563
        );
1564 1564

  
1565
        $gallery_html = compose_cdm_media_gallerie(array(
1565
        $gallery_html = compose_cdm_media_gallery(array(
1566 1566
          'mediaList' => $mediaList,
1567 1567
          'galleryName' => $gallery_name,
1568 1568
          'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
......
1596 1596
          '#uri' => t('open media'),
1597 1597
        );
1598 1598

  
1599
        $gallery_html = compose_cdm_media_gallerie(array(
1599
        $gallery_html = compose_cdm_media_gallery(array(
1600 1600
          'mediaList' => $mediaList,
1601 1601
          'galleryName' => $gallery_name,
1602 1602
          'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
modules/cdm_dataportal/includes/occurrences_new.inc
546 546
    'title',
547 547
    '#uri' => t('open media'),
548 548
  );
549
  $gallery_markup = compose_cdm_media_gallerie(array(
549
  $gallery_markup = compose_cdm_media_gallery(array(
550 550
    'mediaList' => $listOfMedia,
551 551
    'galleryName' => $sob_dto->uuid,
552 552
    'maxExtend' => $gallery_settings['cdm_dataportal_media_maxextend'],
modules/cdm_dataportal/includes/pages.inc
544 544
    }
545 545

  
546 546
    if (isset($representationPart->uri)) {
547
      $profile_image = cdm_media_gallerie_entry_image($representationPart, $taxon_profile_image_settings['maxextend'], FALSE, $attributes);
547
      $profile_image = cdm_media_gallery_entry_image(
548
        ImageMediaItem::fromMediaRepresentationPart($representationPart),
549
        $taxon_profile_image_settings['maxextend'],
550
        FALSE,
551
        $attributes
552
      );
548 553
      // NOTE: style="width:${maxextend}px' is needed for IE8 !!!
549 554
      $max_extend_with = $taxon_profile_image_settings['maxextend'] . 'px';
550 555
      $render_array['taxon_profile_image'] = markup_to_render_array('<div id="taxonProfileImage" style="width:' . $max_extend_with . '">' . $profile_image . '</div>',
modules/cdm_dataportal/lib/FaviconDownloader.php
1
<?php
2
/**
3
 * FaviconDownloader - find favicon URL and download it easy
4
 * Requirements : PHP 5.3+ with curl extension
5
 *
6
 * @author Vincent Paré (www.finalclap.com)
7
 * @copyright © 2014-2015 Vincent Paré
8
 * @license http://opensource.org/licenses/Apache-2.0
9
 * @package FaviconDownloader
10
 * @version 1.0.0
11
 * @tutorial http://www.finalclap.com/faq/477-php-favicon-find-download
12
 */
13

  
14
#namespace Vincepare\FaviconDownloader;
15

  
16
class FaviconDownloader
17
{
18
    // URL types
19
    const URL_TYPE_ABSOLUTE = 1;
20
    const URL_TYPE_ABSOLUTE_SCHEME = 2;
21
    const URL_TYPE_ABSOLUTE_PATH = 3;
22
    const URL_TYPE_RELATIVE = 4;
23
    const URL_TYPE_EMBED_BASE64 = 5;
24
    
25
    public $url;          // (string) Page URL
26
    public $pageUrl;      // (string) Page URL, after prospective redirects
27
    public $siteUrl;      // (string) Site root URL (homepage), based on $pageUrl
28
    public $icoUrl;       // (string) full URI to favicon
29
    public $icoType;      // (string) favicon type (file extension, ex: ico|gif|png)
30
    public $findMethod;   // (string) favicon url determination method (default /favicon.ico or found in head>link tag)
31
    public $error;        // (string) details, in case of failure...
32
    public $icoExists;    // (bool)   tell if the favicon exists (set after calling downloadFavicon)
33
    public $icoMd5;       // (string) md5 of $icoData
34
    public $icoData;      // (binary) favicon binary data
35
    public $debugInfo;    // (array)  additionnal debug info
36
    protected $httpProxy; // (string) HTTP proxy (ex: localhost:8888)
37
    protected $sslVerify; // (bool)   SSL verify peer (default: true)
38
    
39
    /**
40
     * Create a new FaviconDownloader object, search & download favicon if $auto is true
41
     *
42
     * @param string $url Page URL
43
     * @param array $options Optional settings
44
     * @param bool $auto Search & download favicon on instantiation
45
     */
46
    public function __construct($url, $options = null, $auto = true)
47
    {
48
        if (!$url) {
49
            throw new \InvalidArgumentException("url is empty");
50
        }
51
        if (self::urlType($url) != self::URL_TYPE_ABSOLUTE) {
52
            throw new \InvalidArgumentException("'".$url."' is not an absolute url");
53
        }
54
        $this->url = $url;
55
        $this->httpProxy = isset($options['httpProxy']) ? $options['httpProxy'] : null;
56
        $this->sslVerify = isset($options['sslVerify']) && $options['sslVerify'] === false ? false : true;
57
        if ($auto) {
58
            $this->getFaviconUrl();
59
            $this->downloadFavicon();
60
        }
61
    }
62
    
63
    /**
64
     * Download page and search html to find favicon URL. Returns favicon URL.
65
     * HTML parsing is achieved using regular expressions (http://blog.codinghorror.com/parsing-html-the-cthulhu-way/)
66
     * to get it work on all kinds of web documents (including non w3c compliance), which an XML parser can't do.
67
     */
68
    public function getFaviconUrl()
69
    {
70
        // If already executed, don't need to search again
71
        if (!empty($this->icoUrl)) {
72
            return $this->icoUrl;
73
        }
74
        
75
        // Check URL to search
76
        if (empty($this->url)) {
77
            throw new \Exception("url is empty");
78
        }
79
        
80
        // Removing fragment (hash) from URL
81
        $url = $this->url;
82
        $urlInfo = parse_url($this->url);
83
        if (isset($urlInfo['fragment'])) {
84
            $url = str_replace('#'.$urlInfo['fragment'], '', $url);
85
        }
86
        
87
        // Downloading the page
88
        $html = $this->downloadAs($url, $info);
89
        if ($info['curl_errno'] !== CURLE_OK) {
90
            $this->error = $info['curl_error'];
91
            $this->debugInfo['document_curl_errno'] = $info['curl_errno'];
92
            return false;
93
        }
94
        
95
        // Saving final URL (after prospective redirects) and get root URL
96
        $this->pageUrl = $info['effective_url'];
97
        $pageUrlInfo = parse_url($this->pageUrl);
98
        if (!empty($pageUrlInfo['scheme']) && !empty($pageUrlInfo['host'])) {
99
            $this->siteUrl = $pageUrlInfo['scheme'].'://'.$pageUrlInfo['host'].'/';
100
        }
101
        
102
        // Default favicon URL
103
        $this->icoUrl = $this->siteUrl.'favicon.ico';
104
        $this->findMethod = 'default';
105
        
106
        // HTML <head> tag extraction
107
        preg_match('#^(.*)<\s*body#isU', $html, $matches);
108
        $htmlHead = isset($matches[1]) ? $matches[1] : $html;
109
        
110
        // HTML <base> tag href extraction
111
        $base_href = null;
112
        if (preg_match('#<base[^>]+href=(["\'])([^>]+)\1#i', $htmlHead, $matches)) {
113
            $base_href = rtrim($matches[2], '/').'/';
114
            $this->debugInfo['base_href'] = $base_href;
115
        }
116
        
117
        // HTML <link> icon tag analysis
118
        if (preg_match('#<\s*link[^>]*(rel=(["\'])[^>\2]*icon[^>\2]*\2)[^>]*>#i', $htmlHead, $matches)) {
119
            $link_tag = $matches[0];
120
            $this->debugInfo['link_tag'] = $link_tag;
121
            
122
            // HTML <link> icon tag href analysis
123
            if (preg_match('#href\s*=\s*(["\'])(.*?)\1#i', $link_tag, $matches)) {
124
                $ico_href = trim($matches[2]);
125
                $this->debugInfo['ico_href'] = $ico_href;
126
                $this->findMethod = 'head';
127
                
128
                // Building full absolute URL
129
                $urlType = self::urlType($ico_href);
130
                switch($urlType){
131
                    case self::URL_TYPE_ABSOLUTE:
132
                        $this->findMethod .= ' absolute';
133
                        $this->icoUrl = $ico_href;
134
                        $this->icoType = self::getExtension($this->icoUrl);
135
                        break;
136
                    case self::URL_TYPE_ABSOLUTE_SCHEME:
137
                        $this->findMethod .= ' absolute_scheme';
138
                        $this->icoUrl = $pageUrlInfo['scheme'].':'.$ico_href;
139
                        $this->icoType = self::getExtension($this->icoUrl);
140
                        break;
141
                    case self::URL_TYPE_ABSOLUTE_PATH:
142
                        $this->findMethod .= ' absolute_path';
143
                        if (isset($base_href)) {
144
                            $baseHrefType = self::urlType($base_href);
145
                            if ($baseHrefType != self::URL_TYPE_ABSOLUTE) {
146
                                throw new \Exception("Base href is not an absolute URL");
147
                            }
148
                            $baseUrlInfo = parse_url($base_href);
149
                            $this->icoUrl = $baseUrlInfo['scheme'].'://'.$baseUrlInfo['host'].$ico_href;
150
                            $this->findMethod .= ' with base href';
151
                        } else {
152
                            $this->icoUrl = rtrim($this->siteUrl, '/').'/'.ltrim($ico_href, '/');
153
                            $this->findMethod .= ' without base href';
154
                        }
155
                        $this->icoType = self::getExtension($this->icoUrl);
156
                        break;
157
                    case self::URL_TYPE_RELATIVE:
158
                        $this->findMethod .= ' relative';
159
                        $path = preg_replace('#/[^/]+?$#i', '/', $pageUrlInfo['path']);
160
                        if (isset($base_href)) {
161
                            $this->icoUrl = $base_href.$ico_href;
162
                            $this->findMethod .= ' with base href';
163
                        } else {
164
                            $this->icoUrl = $pageUrlInfo['scheme'].'://'.$pageUrlInfo['host'].$path.$ico_href;
165
                            $this->findMethod .= ' without base href';
166
                        }
167
                        $this->icoType = self::getExtension($this->icoUrl);
168
                        break;
169
                    case self::URL_TYPE_EMBED_BASE64:
170
                        $this->findMethod .= ' base64';
171
                        $this->icoUrl = $ico_href;
172
                        break;
173
                }
174
            }
175
        }
176
        
177
        return $this->icoUrl;
178
    }
179
    
180
    /**
181
     * Download the favicon if available
182
     */
183
    public function downloadFavicon()
184
    {
185
        // Check params
186
        if (empty($this->icoUrl)) {
187
            return false;
188
        }
189
        
190
        // Prevent useless re-download
191
        if (!empty($this->icoData)) {
192
            return false;
193
        }
194
        
195
        // Base64 embed favicon
196
        if (preg_match('/^\s*data:(.*?);base64,(.*)/i', $this->icoUrl, $matches)) {
197
            $content = base64_decode($matches[2]);
198
            if ($content === false) {
199
                $this->error = "base64 decode error";
200
                return false;
201
            }
202
            $this->icoData   = $content;
203
            $this->icoMd5    = md5($content);
204
            $this->icoExists = true;
205
            $this->icoType   = self::getExtensionFromMimeType($matches[1]);
206
            return true;
207
        }
208
        
209
        // Download favicon
210
        $content = $this->downloadAs($this->icoUrl, $info);
211
        $this->debugInfo['favicon_download_metadata'] = $info;
212
        
213
        // Failover : if getting a 404 with favicon URL found in HTML source, trying with the default favicon URL
214
        $doFailover = $content === false
215
            && $info['http_code'] == 404
216
            && $this->findMethod != 'default'
217
            && !isset($this->debugInfo['failover']);
218
        if ($doFailover) {
219
            $this->debugInfo['failoverBefore_icoUrl'] = $this->icoUrl;
220
            $this->debugInfo['failoverBefore_findMethod'] = $this->findMethod;
221
            $this->icoUrl = $this->siteUrl.'favicon.ico';
222
            $this->findMethod = 'default';
223
            $this->icoType = self::getExtension($this->icoUrl);
224
            $this->debugInfo['failover'] = true;
225
            return $this->downloadFavicon();
226
        }
227
        
228
        // Download error
229
        if ($content === false) {
230
            $this->error = 'Favicon download error (HTTP '.$info['http_code'].')';
231
            return false;
232
        }
233
        
234
        // Check favicon content
235
        if (strlen($content) == 0) {
236
            $this->error = "Empty content";
237
            return false;
238
        }
239
        $textTypes = array('text/html', 'text/plain');
240
        if (in_array($info['content_type'], $textTypes) || preg_match('#(</html>|</b>)#i', $content)) {
241
            $this->error = "Seems to be a text document";
242
            return false;
243
        }
244
        
245
        // All right baby !
246
        $this->icoData   = $content;
247
        $this->icoMd5    = md5($content);
248
        $this->icoExists = true;
249
        return true;
250
    }
251
    
252
    /**
253
     * Download URL as Firefox with cURL
254
     * Details available in $info if provided
255
     *
256
     * @param string $url URL to download
257
     * @param array $info Download metadata
258
     */
259
    public function downloadAs($url, &$info = null)
260
    {
261
        $ch = curl_init($url);
262
        curl_setopt($ch, CURLOPT_HEADER, false);
263
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
264
        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
265
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);  // Follow redirects (302, 301)
266
        curl_setopt($ch, CURLOPT_MAXREDIRS, 20);         // Follow up to 20 redirects
267
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0');
268
        
269
        // Don't check SSL certificate to allow autosigned certificate
270
        if ($this->sslVerify === false) {
271
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
272
        }
273
        
274
        // Set HTTP proxy
275
        if ($this->httpProxy) {
276
            curl_setopt($ch, CURLOPT_PROXY, $this->httpProxy);
277
        }
278
        
279
        $content = curl_exec($ch);
280
        $info['curl_errno'] = curl_errno($ch);
281
        $info['curl_error'] = curl_error($ch);
282
        $info['http_code'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
283
        $info['effective_url'] = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
284
        $info['redirect_count'] = curl_getinfo($ch, CURLINFO_REDIRECT_COUNT);
285
        $info['content_type'] = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
286
        curl_close($ch);
287
        
288
        if ($info['curl_errno'] !== CURLE_OK || in_array($info['http_code'], array(403, 404, 500, 503))) {
289
            return false;
290
        }
291
        return $content;
292
    }
293
    
294
    /**
295
     * Return file extension from an URL or a file path
296
     *
297
     * @param string $url
298
     */
299
    public static function getExtension($url)
300
    {
301
        if (preg_match('#^(https?|ftp)#i', $url)) {
302
            $purl = parse_url($url);
303
            $url = $purl['path'];
304
        }
305
        $info = pathinfo($url);
306
        return $info['extension'];
307
    }
308
    
309
    /**
310
     * Return file extension from MIME type
311
     *
312
     * @param string $mimeType
313
     */
314
    public static function getExtensionFromMimeType($mimeType)
315
    {
316
        $typeMapping = array(
317
            'ico' => '#image/(x-icon|ico)#i',
318
            'png' => '#image/png#i',
319
            'gif' => '#image/gif#i',
320
            'jpg' => '#image/jpe?g#i',
321
        );
322
        foreach ($typeMapping as $key => $val) {
323
            if (preg_match($val, $mimeType)) {
324
                return $key;
325
            }
326
        }
327
        return 'ico';
328
    }
329
    
330
    /**
331
     * Return URL type, either :
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff