EGOCMS  24.0
EGOTEC Content-Managament-System
Ego_Output.php
gehe zur Dokumentation dieser Datei
1 <?php
7 require_once 'base/Ego_System.php';
8 require_once 'base/functions.php';
9 
28 class Ego_Output {
34  private $page = null;
35 
41  private $site = null;
42 
48  private $file = '';
49 
55  private $info = array();
56 
62  private $pool = '';
63 
69  private $mime_type = '';
70 
76  private $cache = '';
77 
83  private $generator = 'Server-File';
84 
90  private $etag = '';
91 
97  private $name = '';
98 
104  private $is_image = false;
105 
111  private $is_valid = false;
112 
118  private $web_format = false;
119 
125  private $temporary = false;
126 
132  private $quality = 100;
133 
139  private $multipart_support = false;
140 
146  public static $convert_types = [
147  'avif' => [
148  'label' => 'Avif',
149  'mime' => 'image/avif'
150  ],
151  'webp' => [
152  'label' => 'WebP',
153  'mime' => 'image/webp'
154  ]
155  ];
156 
162  private $converted = '';
163 
169  private $error = false;
170 
178  public function __construct($source = null, $pool = '', $dir = '') {
179  if (is_a($source, 'Page')) {
180  // Datei einer Seite
181  $this->page = $source;
182  $this->site = $this->page->getSite();
183 
184  // Mediapool Unterverzeichnis erkennen
185  if ($dir == '' && ($_REQUEST['dir'] || $_REQUEST['c_date'])) {
186  $dir = $_REQUEST['dir'] ?: $_REQUEST['c_date'];
187  if ($_REQUEST['c_date'] && !is_numeric($dir) && strpos($dir, DIRECTORY_SEPARATOR) === false) {
188  // Mediapool Archive erwarten immer Zeitstempel
189  $dir = strtotime($dir);
190  }
191  }
192 
193  if ($_REQUEST['thumbnail']) {
194  // Ein Thumbnail über die URL generieren
195  [$width, $height, $index] = explode('x', $_REQUEST['thumbnail'], 3);
196  $this->file = $this->getThumbnail((int) $width, (int) ($height ?? $width), $pool, $dir, [
197  'index' => (int) $index
198  ]);
199  $this->detectMimeType();
200  $this->is_image = true;
201  if ($pool) {
202  $this->pool = $pool;
203  }
204  } elseif ($pool) {
205  $this->pool = $pool;
206  $file = $this->page->getMediapool()->get($this->pool, $dir);
207  $this->info = $file;
208  if (!$file['quarantine']) {
209  $this->file = $file['file'];
210  }
211 
212  if (
213  $this->info['convert']
214  && $this->info['isImage']
215  && isset(self::$convert_types[$this->info['suffix']])
216  ) {
217  // Automatische Umwandlung durchführen
218  $this->convertImage($this->info['suffix'], (bool) $_REQUEST['reset']);
219  } else if ($this->info['isVideo'] && $this->site->admin['mediapool']['video_compress'] && !$GLOBALS['is_index']) {
220  // Automatisch zur passenden Datei für das Endgerät wechseln
221  require_once 'base/Ego_Mobile.php';
222  $mobile = new Ego_Mobile();
223  $is_mobile = $mobile->isMobile();
224  $is_tablet = $mobile->isTablet();
225 
226  $files = [];
227 
228  foreach (Ego_System::VIDEO_RESOLUTIONS as $height => $scale) {
229  $files["{$height}p"] = $this->page->getMediapool()->get("{$this->info['title']}_{$height}p.mp4", '_compress');
230  }
231 
232  $files = array_reverse($files);
233 
234  if (
235  ($is_mobile && !$is_tablet && ($data = $files['360p'])) // 360p nur für Mobile
236  || ($is_tablet && ($data = $files['720p'])) // 720p nur für Tablets
237  || ($data = $files['1080p']) // 1080p nur für Desktops
238  ) {
239  $this->file = $data['file'];
240  $this->mime_type = $data['mime'];
241  } else {
242  // Wenn nichts zutrifft, dann die höhste verfügbare Auflösung verwenden (ansonsten die Originaldatei)
243  $existing_files = array_filter($files);
244  if ($data = array_pop($existing_files)) {
245  $this->file = $data['file'];
246  $this->mime_type = $data['mime'];
247  }
248  }
249  } else {
250  $this->detectMimeType();
251  }
252  $type = $file['suffix'];
253  } elseif (!$this->page->extra['quarantine']) {
254  // Bei der Ausgabe eines Multimedia Ausschnitts wird die Originalseite referenziert
255  if ($this->page->field['type'] == 'multimedia/image' && $this->page->extra['crop_image']) {
256  $extra = array(
257  'edit' => $this->page->extra['edit'],
258  'crop_image' => true
259  );
260  $this->page = $this->page->getParents()->nextPage();
261  $this->page->extra = array_merge($this->page->extra, $extra);
262  }
263 
264  $this->file = $GLOBALS['egotec_conf']['var_dir'].'media/'.$this->site->name.'/';
265  if ($_REQUEST['c_date']) {
266  // Als "c_date" das vom Page Konstruktor ermittelte Datum verwenden
267  $archive_file = $this->file . $this->page->getMediaFilename(
268  false,
269  '_'.Ego_System::dateEncode($this->page->field['c_date'])
270  );
271  if (Ego_System::file_exists($archive_file)) {
272  $this->file = $archive_file;
273  } else {
274  // Gibt es keine passende Version, wird das Original verwendet
275  $this->file .= $this->page->getMediaFilename();
276  }
277  } else {
278  $this->file .= $this->page->getMediaFilename();
279  }
280  $this->mime_type = $this->page->extra['mime_type'];
281 
282  if (
283  $this->page->field['type'] == 'multimedia/image'
284  && isset(self::$convert_types[ltrim($_SERVER['REQUEST_SUFFIX'], '.')])
285  && !in_array($this->mime_type, ['image/svg+xml', self::$convert_types[ltrim($_SERVER['REQUEST_SUFFIX'], '.')]['mime']])
286  ) {
287  // Automatische Umwandlung durchführen
288  $this->convertImage($type = $this->page->extra['image_type'] = ltrim($_SERVER['REQUEST_SUFFIX'], '.'), (bool) $_REQUEST['reset']);
289  } else {
290  $type = $this->page->extra['image_type'];
291  }
292 
293  // Automatisch zur passenden Datei für das Endgerät wechseln
294  if ($this->site->admin['video']['compress'] && !$GLOBALS['is_index']) {
295  require_once 'base/Ego_Mobile.php';
296  $mobile = new Ego_Mobile();
297  $files = [
298  '360p' => $this->page->getMediapool()->get('360p.mp4'),
299  '720p' => $this->page->getMediapool()->get('720p.mp4'),
300  '1080p' => $this->page->getMediapool()->get('1080p.mp4')
301  ];
302  $is_mobile = $mobile->isMobile();
303  $is_tablet = $mobile->isTablet();
304  if (
305  ($is_mobile && !$is_tablet && ($data = $files['360p'])) // 360p nur für Mobile
306  || ($is_tablet && ($data = $files['720p'])) // 720p nur für Tablets
307  || ($data = $files['1080p']) // 1080p nur für Desktops
308  ) {
309  $this->file = $data['file'];
310  $this->mime_type = $data['mime'];
311  } else {
312  // Wenn nichts zutrifft, dann die höhste verfügbare Auflösung verwenden (ansonsten die Originaldatei)
313  $existing_files = array_filter($files);
314  if ($data = array_pop($existing_files)) {
315  $this->file = $data['file'];
316  $this->mime_type = $data['mime'];
317  }
318  }
319  }
320  }
321 
322  // Bestimmte MimeTypes setzen
323  if (!empty($type)) {
324  $formats = array(
325  'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
326  'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
327  'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
328  'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
329  'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
330  'xps' => 'application/vnd.ms-xpsdocument',
331  'svg' => 'image/svg+xml'
332  );
333  if (array_key_exists($type, $formats)) {
334  $this->mime_type = $formats[$type];
335  }
336  }
337  } elseif (is_string($source)) {
338  if (strpos($source, 'string:') === 0) {
339  // Übergebenen String in eine temporäre Datei speichern
340  $file = tempnam($GLOBALS['egotec_conf']['tmp_dir'], 'output');
341  Ego_System::file_put_contents($file, substr($source, 7));
342  $this->setFile($file);
343  $this->setTemporary(true);
344  } else {
345  // Existierende Datei verwenden
346  $this->setFile($source);
347  }
348  if (!empty($GLOBALS['site']) && is_a($GLOBALS['site'], 'Site')) {
349  $this->site = $GLOBALS['site'];
350  } elseif (class_exists('Site')) {
351  $this->site = new Site();
352  }
353  }
354 
355  // Erkennen, ob die auszugebende Datei ein Bild (außer SVG und ICO: diese werden immer direkt und ohne ImageMagick Transformation ausgegeben)
356  $ignore_image_mimes = array('image/svg+xml', 'image/x-icon', 'image/vnd.microsoft.icon');
357  $ignore_image_types = array('svg', 'ico');
358 
359  if (!$this->is_image) {
360  if ($this->page && $this->page->field['type'] == 'multimedia/image') {
361  $this->is_image = !in_array($this->page->extra['mime_type'], $ignore_image_mimes);
362  } elseif ($this->file) {
363  if ($pool) {
364  $this->is_image = preg_match(
365  '/\.(' . implode(
366  '|',
367  array_filter(
368  Ego_System::getFormats('image'),
369  function ($type) use ($ignore_image_types) {
370  return !in_array($type, $ignore_image_types);
371  }
372  )
373  ) . ')$/ims',
374  $pool
375  );
376  } else {
377  $this->is_image = !(strpos($this->mime_type, 'image') === false || in_array($this->mime_type, $ignore_image_mimes));
378  if ($this->page && $this->page->extra['mime_type'] && $this->page->extra['image_type']) {
379  $this->is_valid = true; // Kopierte Multimedia Seitentypen unterstützen
380  }
381  }
382  }
383  }
384 
385  if ($this->is_image) {
386  $this->web_format = in_array(
387  $this->mime_type,
389  );
390  if (is_numeric($_REQUEST['quality'])) {
391  // In der URL gesetzt
392  $this->quality = (int) $_REQUEST['quality'];
393  } else if (is_numeric($GLOBALS['quality'])) {
394  // Vom Template gesetzt
395  $this->quality = (int) $GLOBALS['quality'];
396  } elseif (is_numeric($GLOBALS['egotec_conf']['image']['quality'])) {
397  // Aus den Einstellungen des Mandanten auslesen
398  $this->quality = (int) $GLOBALS['egotec_conf']['image']['quality'];
399  }
400  }
401  }
402 
409  private function isValid($download = false) {
410  return (
411  $this->is_valid
412  || (
413  (
414  in_array($this->page->field['type'], array('multimedia/file', 'multimedia/image', 'multimedia/category'))
415  || $this->pool
416  )
417  && (
418  (
419  $download
420  && $this->page->field['type'] == 'multimedia/category'
421  )
422  || Ego_System::file_exists($this->file)
423  )
424  && $this->page->hasRights('view')
425  )
426  );
427  }
428 
436  public function convertImage($format, $reset = false) {
437  if ($this->file && $this->page) {
438  $cache_file = $GLOBALS['egotec_conf']['var_dir']
439  . 'converted/'
440  . $this->page->getSite()->name . '/'
441  . $this->page->getSite()->language . '/'
442  . $this->page->field['id'] . '/'
443  . $format . '_' . md5($this->file) . '_' . md5(filemtime($this->file));
444 
445  // Mime-Type ändern
446  if (isset(self::$convert_types[$format])) {
447  $this->mime_type = self::$convert_types[$format]['mime'];
448  }
449 
450  // Zwischengespeicherte Datei verwenden
451  if (!$reset && Ego_System::file_exists($cache_file)) {
452  $this->file = $cache_file;
453  } else {
454  $this->clearConvertedImages($format);
455 
456  // Datei neu umwandeln
457  $tmp_file = tempnam($GLOBALS['egotec_conf']['tmp_dir'], 'convert');
458  Ego_System::copy($this->file, $tmp_file);
459 
460  // Zu verwendende Methode für die Umwandlung bestimmen
461  $method = $GLOBALS['egotec_conf']['files']["convert_$format"] ?: '';
462 
463  switch ($method) {
464  case 'avifenc':
465  Ego_System::mkdir(dirname($cache_file));
466  Ego_System::exec('avifenc', [
467  '--jobs ' . ($GLOBALS['egotec_conf']['files']['max_cores'] ?: 1),
468  '--min 0',
469  '--max 63',
470  '-a end-usage=q',
471  '-a cq-level=32',
472  '-a tune=ssim',
473  '-a deltaq-mode=3',
474  '-a sharpness=3',
475  '-y 420',
476  $tmp_file,
477  $cache_file
478  ]);
479 
480  // ...und zwischenspeichern
481  $this->file = $cache_file;
482  break;
483 
484  default:
485  require_once 'base/Ego_Image.php';
486  $image = new Ego_Image($tmp_file);
487 
488  if ($converted_file = $image->convert($format, [
489  'output' => $cache_file
490  ])) {
491  // ...und zwischenspeichern
492  $this->file = $converted_file;
493  }
494  }
495 
496  @unlink($tmp_file);
497  }
498 
499  // Merken, dass diese Datei umgewandelt wurde
500  $this->converted = $format;
501  $this->name = preg_replace('/\.[^.]+$/', ".$format", $this->getName());
502  }
503  return $this->converted != '';
504  }
505 
512  public function clearConvertedImages($format = '') {
513  if ($this->file && $this->page) {
514  foreach (glob(
515  $GLOBALS['egotec_conf']['var_dir']
516  . 'converted/'
517  . $this->page->getSite()->name . '/'
518  . $this->page->getSite()->language . '/'
519  . $this->page->field['id'] . '/'
520  . ($format ?: '[!_]*') . '_' . md5($this->file) . '_*'
521  ) as $file) {
522  @unlink($file);
523  }
524  }
525  }
526 
532  public function getConvertedImages() {
533  $files = [];
534 
535  if ($this->file && $this->page) {
536  foreach (glob(
537  $GLOBALS['egotec_conf']['var_dir']
538  . 'converted/'
539  . $this->page->getSite()->name . '/'
540  . $this->page->getSite()->language . '/'
541  . $this->page->field['id'] . '/'
542  . '[!_]*_' . md5($this->file) . '_*'
543  ) as $file) {
544  if (preg_match('/^([^_]+)_/', basename($file), $matches)) {
545  $files[] = [
546  'format' => $format = $matches[1],
547  'label' => self::$convert_types[$format]['label'],
548  'size' => Ego_System::byte_format(@filesize($file)),
549  'file' => $file
550  ];
551  }
552  }
553  }
554 
555  return $files;
556  }
557 
563  private function detectMimeType() {
564  require_once 'base/Ego_MimeType.php';
565  $mime = new Ego_MimeType();
566  $this->mime_type = $mime->autoDetect($this->file);
567  }
568 
576  public function error($image = false, $code = 404) {
577  Ego_System::header($code);
578 
579  $forced_error_image = [];
580  if (($this->is_image || $image || !empty($forced_error_image = $this->forceErrorImage())) && !$this->error) {
581  $error_image = true;
582  if (class_exists('Imagick')) {
583  $width = $this->page ? $this->page->extra['origImgWidth'] : 0;
584  if (!is_numeric($width) || $_REQUEST['width']) {
585  $width = (int) ($_REQUEST['width'] ?? $forced_error_image['width']);
586  }
587  $height = $this->page ? $this->page->extra['origImgHeight'] : 0;
588  if (!is_numeric($height) || $_REQUEST['height']) {
589  $height = (int) ($_REQUEST['height'] ?? $forced_error_image['height']);
590  }
591 
592  if ($width && $height) {
593  $this->file = tempnam($GLOBALS['egotec_conf']['tmp_dir'], 'transform');
594 
595  $image = new Imagick();
596  $draw = new ImagickDraw();
597  $image->newImage($width, $height, new ImagickPixel('grey'));
598 
599  $font_size = ceil(min(array($width, $height)) / 6);
600  $draw->setFillColor('lightgrey');
601  $draw->setFontSize($font_size);
602  $draw->setTextAlignment(Imagick::ALIGN_CENTER);
603  $image->annotateImage(
604  $draw,
605  ceil($width / 2),
606  ceil($height / 2) + ceil($font_size / 2),
607  0,
608  Ego_System::isDevMode() ? ($forced_error_image['text'] ?? "{$width} x {$height}") : $code
609  );
610  $image->setImageFormat('png');
611  $image->writeImage($this->file);
612 
613  $this->setTemporary(true);
614 
615  $error_image = false;
616  }
617  }
618  if ($error_image) {
619  $this->file = $GLOBALS['egotec_conf']['egotec_dir'] . 'pub' .
620  DIRECTORY_SEPARATOR . 'missing_file.gif';
621  }
622  $this->detectMimeType();
623  $this->is_valid = true;
624  $this->error = true;
625  $error = $this->read();
626  } elseif ($this->site) {
627  $error = $this->site->getErrorPage();
628  } else {
629  $site = new Site();
630  $error = $site->getErrorPage();
631  }
632  return $error;
633  }
634 
642  private function forceErrorImage() {
643  $result = [];
644 
645  if (
646  $this->mime_type == 'image/svg+xml'
647  || ($this->pool && mb_substr(mb_strtolower($this->pool), -4) == '.svg')
648  ) {
649  $result = [
650  'width' => 1024,
651  'height' => 768,
652  'text' => 'SVG'
653  ];
654  }
655 
656  return $result;
657  }
658 
668  private function prepend(&$page, &$site, &$auth, &$smarty) {
669  if (
670  !$this->pool
671  && $page
672  && $site
673  && ($file = $site->getSiteFile($page->field['type'].DIRECTORY_SEPARATOR.'index.php'))
674  ) {
675  require_once($file);
676  }
677  }
678 
685  public function setName($name) {
686  $this->name = $name;
687  }
688 
694  public function getName() {
695  if ($this->name != '') {
696  return $this->name;
697  } elseif ($this->pool) {
698  return $this->pool;
699  } elseif ($this->page) {
700  $suffix = '.'.$this->page->extra['image_type'];
701  return preg_replace(
702  '/'.preg_quote($suffix, '/').'$/i',
703  '',
704  strtr($this->page->field['name'], '/\:*?"<>|', '_________')
705  ).$suffix;
706  } else {
707  return basename($this->file);
708  }
709  }
710 
717  private function setDefaultHeaders($length = 0) {
718  if (!empty($this->etag)) {
719  Ego_System::header('ETag: "'.$this->etag.'"');
720  }
721  Ego_System::header('Content-Type: '.$this->mime_type.'; charset=UTF-8');
722  $t = $this->page ? $this->site->getCacheExpire() : 0;
723  if (!$t) { // Keiner darf cachen
725  } else {
726  /* Bei Bildern ist das Feld cache immer 0. Es wird trotzdem immer ein Header gesendet,
727  * so dass Browser und Proxy cachen dürfen. */
728  Ego_System::header("Expires: ". gmdate("D, d M Y H:i:s", time() + $t) . " GMT");
729 
730  /* Bei HTTPS und PDF muss für IE8 (und kleiner) immer "Pragma: public" stehen
731  * ansonsten gibt es komische Probleme bei "Ziel speichern unter"
732  * und Seitenaufruf von PDFs bei HTTPS */
733  if (
734  $_SERVER['HTTPS'] == 'on'
735  && in_array(
736  mb_strtolower($this->page->extra['image_type']),
737  array('pdf', 'doc', 'docx', 'dot')
738  )
739  ) {
740  Ego_System::header("Pragma: public");
741  }
742  if (
743  $this->page->field['cache']&2
744  && $GLOBALS['auth']->isNobody()
745  ) {
746  // Proxy und/oder Browser darf cachen
747  Ego_System::header('Cache-Control: public');
748  } else {
749  // Nur Browser darf cachen
750  Ego_System::header('Cache-Control: private');
751  }
752  }
753  if (!$GLOBALS['egotec_conf']['no_content_length_header']) {
754  if (!$length) {
755  $length = $this->getFilesize($this->file);
756  }
757  Ego_System::header('Content-Length: '.$length);
758  }
759  if ($this->page) {
760  Ego_System::header("Last-Modified: ".gmdate("D, d M Y H:i:s", Ego_System::dateEncode($this->page->field['c_date']))." GMT");
761  if ($this->page->extra['meta_robots'] && $this->page->extra['meta_robots'] != 'index') {
762  Ego_System::header('X-Robots-Tag: '.$this->page->extra['meta_robots']);
763  }
764  }
765  }
766 
772  private function setSingleHeaders() {
773  Ego_System::header('Content-Disposition: inline; filename="'.$this->getName().'";');
774  Ego_System::header('Content-Transfer-Encoding: binary');
775  Ego_System::header('Accept-Ranges: bytes');
776  Ego_System::header('Content-Size: '.$this->getFilesize($this->file));
777  Ego_System::header('Connection: Keep-Alive');
778  $this->setDefaultHeaders();
779  ob_clean();
780  flush();
781  readfile_chunked($this->file);
782  }
783 
789  private function setRangeHeaders() {
790  $fp = @fopen($this->file, 'rb');
791  $size = $this->getFilesize($this->file);
792  $length = $size;
793  $start = 0;
794  $end = $size - 1;
795  if (isset($_SERVER['HTTP_RANGE'])) {
796  $c_end = $end;
797  [, $range] = explode('=', $_SERVER['HTTP_RANGE'], 2);
798  if (strpos($range, ',') !== false) {
799  // Multiple Range
800  if ($this->multipart_support) {
801  $boundary = md5('multipart_byteranges');
802  Ego_System::header(206);
803  Ego_System::header("Content-Length: $size");
804  Ego_System::header('Content-Type: multipart/byteranges; boundary=' . $boundary);
805  foreach (explode(',', $range) as $multi_range) {
806  $multi_range = explode('-', trim($multi_range));
807  if ($multi_range[0] == '-') {
808  $start = $size - substr($multi_range, 1);
809  $end = $c_end;
810  } else {
811  $start = $multi_range[0];
812  $end = (isset($multi_range[1]) && is_numeric($multi_range[1])) ? $multi_range[1] : $size;
813  }
814  fseek($fp, $start);
815  $buffer = 1024 * 8;
816  echo "--{$boundary}\r\n";
817  echo "Content-Type: {$this->mime_type}\r\n";
818  echo "Content-Range: bytes $start-$end/$size\r\n\r\n";
819  while (!feof($fp) && ($p = ftell($fp)) <= $end) {
820  if ($p + $buffer > $end) {
821  $buffer = $end - $p + 1;
822  }
823  set_time_limit(0);
824  echo fread($fp, $buffer);
825  }
826  echo "\r\n";
827  flush();
828  }
829  echo "--{$boundary}--\r\n\r\n";
830  fclose($fp);
831  } else {
832  // Multiple Range nicht unterstützen und stattdessen als Download behandeln
833  $this->setDownloadHeaders();
834  }
835  return;
836  } else {
837  // Single Range
838  if ($range[0] == '-') {
839  $c_start = $size - substr($range, 1);
840  } else {
841  $range = explode('-', $range);
842  $c_start = $range[0];
843  $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
844  }
845  $c_end = $c_end > $end ? $end : $c_end;
846  if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
847  Ego_System::header(416);
848  Ego_System::header("Content-Range: bytes $start-$end/$size");
849  exit;
850  }
851  $start = $c_start;
852  $end = $c_end;
853  $length = $end - $start + 1;
854  fseek($fp, $start);
855  Ego_System::header(206);
856  }
857  }
858  Ego_System::header("Content-Range: bytes $start-$end/$size");
859  $this->setDefaultHeaders($length);
860  $buffer = 1024 * 8;
861  while (!feof($fp) && ($p = ftell($fp)) <= $end) {
862  if ($p + $buffer > $end) {
863  $buffer = $end - $p + 1;
864  }
865  set_time_limit(0);
866  echo fread($fp, $buffer);
867  flush();
868  }
869  fclose($fp);
870  }
871 
877  private function setDownloadHeaders() {
878  Ego_System::header('Content-Description: File Transfer');
879  Ego_System::header('Content-Disposition: attachment; filename="'.$this->getName().'";');
880  Ego_System::header('Content-Transfer-Encoding: binary');
881  Ego_System::header('Connection: close');
882  $this->setDefaultHeaders();
883  ob_clean();
884  flush();
885  readfile_chunked($this->file);
886  }
887 
894  public function read($download = false) {
895  // Datei aus dem Cache ermitteln
896  if (
897  !empty($this->etag)
898  && $_SERVER['HTTP_IF_NONE_MATCH'] == '"' . $this->etag . '"'
899  && $GLOBALS['auth']->isNobody()
900  ) {
901  Ego_System::header('Cache-Control: private');
902  Ego_System::header(304);
903  exit;
904  }
905 
906  if ($this->isValid($download)) {
907  $this->prepend($this->page, $this->site, $GLOBALS['auth'], $GLOBALS['smarty']);
908 
909  // In der Entwicklungsumgebung immer Platzhalter Bilder anzeigen
910  if (
911  $GLOBALS['egotec_conf']['dev']['images']
912  && $this->is_image
913  && !$download
914  ) {
915  $this->error(true);
916  }
917 
918  // Parameter für die Modifizierung ermitteln
919  $modify = array();
920  $modify_keys = array('clip', 'rotation', 'mirror', 'grayscale');
921  if ($_REQUEST['original'] && !$GLOBALS['auth']->isNobody()) {
922  // Angemeldete Benutzer dürfen das Original sehen
923  unset($_REQUEST['width'], $_REQUEST['height']);
924 
925  // Im Original wird die Rotation beibehalten
926  $modify_keys = array('rotation', 'mirror', 'grayscale');
927  }
928 
929  foreach ($modify_keys as $param) {
930  if (isset($_REQUEST[$param])) {
931  $modify[$param] = $_REQUEST[$param];
932  } else {
933  if ($this->pool) {
934  if (isset($this->info[$param]) && !empty($this->info[$param])) {
935  $modify[$param] = $this->info[$param];
936  }
937  } elseif ($this->page->field['type'] == 'multimedia/image') {
938  if (isset($this->page->extra['edit'][$param])) {
939  $modify[$param] = $this->page->extra['edit'][$param];
940  }
941  }
942  }
943  }
944 
945  if (
946  (!$download || !empty($modify))
947  && $this->is_image
948  && ($_REQUEST['width']
949  || $_REQUEST['height']
950  || $this->quality != 100
951  || !$this->web_format
952  || !empty($modify))
953  ) {
954  // Bild transformieren
955  try {
956  $this->transform((int) $_REQUEST['width'], (int) $_REQUEST['height'], $modify);
957  } catch (Ego_Image_Exception $e) {
958  $this->error(true, 406);
959  }
960  }
961  if ($download) {
962  if (
963  !$this->page
964  && @is_dir($this->file)
965  ) {
966  // Bei einem Verzeichnis wird automatisch ein Archiv erzeugt
967  $this->setTemporary(true);
968  $this->setFile($this->createArchive($this->file));
969  } elseif (
970  $this->page
971  && $this->page->field['type'] == 'multimedia/category'
972  && !empty($this->site->admin['allow_download'])
973  && !empty($this->page->extra['allow_download'])
974  ) {
975  // Archiv erstellen
976  $this->file = $this->page->export();
977  $this->detectMimeType();
978  $this->setTemporary(true);
979  $this->page->extra['image_type'] = 'tar.gz';
980  }
981  // Datei herunterladen
982  $this->setDownloadHeaders();
983  } else {
984  // Datei ausgeben
985  $partial = false;
986  if (
987  ( // Ehemals immer für Videos, zur Kontrolle -> Issue #2010
988  isset($_SERVER['HTTP_RANGE']) // Range Header werden angefordert
989  )
990  && (
991  !preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $matches)
992  || $matches[1] > 8 // Range Header nicht für MSIE 8 und kleiner
993  )
994  ) {
995  // teilweise
996  $this->setRangeHeaders();
997  $partial = true;
998  } else {
999  // direkt
1000  $this->setSingleHeaders();
1001  }
1002  if (
1003  !$partial
1004  && $this->site
1005  && !empty($this->cache)
1006  && ($this->page->field['cache']&3) != 0
1007  && !$GLOBALS['no_cache']
1008  ) {
1009  // Ausgabe cachen
1010  $info = array(
1011  'headers' => $GLOBALS['egotec']['response_headers'],
1012  'stats' => $GLOBALS['stats'],
1013  'type' => $this->page->field['type']
1014  );
1015  $this->site->setCacheEntry($this->cache . '.info', $info);
1016  $this->site->setCacheEntry($this->cache, $this->file);
1017  }
1018  }
1019  if ($this->temporary) {
1020  @unlink($this->file);
1021  }
1022  exit;
1023  }
1024  return $this->error($this->page && strpos($this->page->extra['mime_type'], 'image/') === 0);
1025  }
1026 
1032  public function download() {
1033 
1034  return $this->read(true);
1035  }
1036 
1048  public function pdf($name = '', $temporary = true, $cookie = true, $return = 1) {
1049  if ($this->isValid()) {
1050  // Einstellungen
1051  $smarty = $GLOBALS['smarty'];
1052  $site = $this->site;
1053  $page = $this->page;
1054  $context = null;
1055  if ($cookie && !empty($_COOKIE[EGOTEC])) {
1056  $context = stream_context_create(array(
1057  'http' => array(
1058  'method' => 'GET',
1059  'header' => "Cookie: ".EGOTEC."=".$_COOKIE[EGOTEC]."\r\n"
1060  )
1061  ));
1062  }
1063  $s = Ego_System::file_get_contents($this->file, true, $context);
1064  if (!empty($name)) {
1065  $this->setName($name);
1066  }
1067  $_REQUEST['title'] = $this->getName();
1068 
1069  // Generierung durchführen
1070  $no_pdf_header = true;
1071  include('pdf/pdf_create.php');
1072 
1073  // Datei übernehmen
1074  $pdf_file = $GLOBALS['egotec_conf']['tmp_dir'].'output'.md5(microtime()).'.pdf';
1075  Ego_System::file_put_contents($pdf_file, $s);
1076  if ($temporary) {
1077  $this->removeFile();
1078  }
1079  $this->setTemporary($return !== 3);
1080  $this->setFile($pdf_file);
1081 
1082  // Datei ausgeben
1083  switch ($return) {
1084  case 1:
1085  // Download
1086  return $this->download();
1087  case 2:
1088  // Ausgabe
1089  return $this->read();
1090  case 3:
1091  // Pfad zur generierten PDF Datei
1092  return $this->file;
1093  }
1094  }
1095  return $this->error();
1096  }
1097 
1106  public function transform($width, $height, $modify = array()) {
1107  if ($this->is_image) {
1108  // Hook für eigene Transformationen erkennen
1109  $output_transform = $this->page && method_exists($this->page, 'outputTransform');
1110 
1111  // Wasserzeichen erkennen
1112  $output_watermark = '';
1113  if ($this->pool) {
1114  // Mediapool (ohne Vererbung)
1115  $list = md5($_REQUEST['dir']);
1116  $key = md5($this->pool);
1117  if ($this->page->extra['mediapool'][$list][$key]['watermark']) {
1118  $output_watermark = $this->page->extra['mediapool'][$list][$key]['watermark'];
1119  }
1120  } elseif ($this->page) {
1121  // Multimedia (mit Vererbung)
1122  $inherit = $this->page->inheritExtra(array('watermark'));
1123  if ($inherit['watermark']) {
1124  $output_watermark = $inherit['watermark'];
1125  }
1126  }
1127 
1128  // Kompressionen sind nur mit JPG Dateien möglich
1129  $quality_mime_types = array('image/jpeg', 'image/jpg', 'image/webp', 'image/avif');
1130  $quality = in_array($this->mime_type, $quality_mime_types) ? $this->quality : 0;
1131 
1132  if ($width > 0 || $height > 0 || !empty($modify) || $output_transform || $output_watermark || $quality) {
1133  require_once 'base/Ego_Image.php';
1134  $size = Ego_Image::getDimensions($this->file);
1135 
1136  // Breite und Höhe dürfen nicht größer sein als das Original
1137  if ($width > $size[0] || $height > $size[1]) {
1138  $width = $size[0];
1139  $height = $size[1];
1140  }
1141 
1142  if (!$_REQUEST['max'] || $size[0] > $width || $size[1] > $height || !empty($modify)) {
1143  $dir = $GLOBALS['egotec_conf']['cachemedia_dir'];
1144  if ($this->page) {
1145  $dir .= $this->site->name . DIRECTORY_SEPARATOR . $this->site->language . DIRECTORY_SEPARATOR . $this->page->field['id'] . DIRECTORY_SEPARATOR;
1146  $dir .= Ego_System::dateEncode($_REQUEST['c_date'] ? $_REQUEST['c_date'] : $this->page->field['c_date']) .
1147  DIRECTORY_SEPARATOR;
1148  } else {
1149  $dir .= '_output' . DIRECTORY_SEPARATOR;
1150  }
1151  Ego_System::mkdir($dir);
1152 
1153  $file = $dir . $width . 'x' . $height . 'x' . $this->quality . ($this->web_format ? '' : '_jpg');
1154  if ($this->pool) {
1155  $file .= '_' . str_replace('.', '_', base64_encode($this->pool));
1156  } elseif (!$this->page) {
1157  $file .= '_' . md5($this->file);
1158  }
1159  if ($output_watermark) {
1160  $file .= '_' . md5($output_watermark);
1161  }
1162  if ($this->converted) {
1163  $file .= '_' . $this->converted;
1164  }
1165  if (!empty($modify)) {
1166  $file .= '_' . md5(serialize($modify));
1167  }
1168 
1169  // Hook um abzufragen ob die Cache verwendet werden soll
1170  $output_cache = $this->page && method_exists($this->page, 'outputCache');
1171 
1172  if (
1173  !$_REQUEST['c_date']
1174  && !$this->error
1175  && (!$output_cache || $this->page->outputCache($width, $height))
1176  && Ego_System::file_exists($file)
1177  ) {
1178  // Datei aus dem Cache verwenden
1179  $this->file = $file;
1180  Ego_System::header("X-Cache: CMS");
1181  } else {
1182  // Keine unnötige Skalierung bei Originalgröße und voller Qualität
1183  if (
1184  !$_REQUEST['c_date']
1185  && $size[0] == $width
1186  && $size[1] == $height
1187  && empty($modify)
1188  && (!in_array($this->mime_type, $quality_mime_types)
1189  || $this->quality == 100)
1190  && !$output_transform
1191  && !$output_watermark
1192  ) {
1193  return false;
1194  }
1195 
1196  // Datei neu generieren
1197  $imageTransform = new Ego_Image();
1198  $tmp = tempnam($GLOBALS['egotec_conf']['tmp_dir'], 'transform');
1199 
1200  if (Ego_System::copy($this->file, $tmp)) {
1201  $imageTransform->load($tmp);
1202 
1203  // Ausgabe modifizieren
1204  if (!empty($modify)) {
1205  // Bild spiegeln
1206  if ($modify['mirror']) {
1207  if (strpos($modify['mirror'], 'v') !== false) {
1208  $imageTransform->mirror(true, false);
1209  }
1210  if (strpos($modify['mirror'], 'h') !== false) {
1211  $imageTransform->mirror(false, true);
1212  }
1213  }
1214 
1215  // Bild rotieren
1216  if ($modify['rotation']) {
1217  $imageTransform->rotate((int) $modify['rotation']);
1218  }
1219 
1220  // Bildausschnitt verwenden
1221  if ($modify['clip']) {
1222  [$x1, $y1, $x2, $y2] = explode(',', $modify['clip'], 4);
1223  $cwidth = $x2 - $x1;
1224  $cheight = $y2 - $y1;
1225 
1226  $imageTransform->crop($x1, $y1, $x2, $y2);
1227 
1228  $size[0] = $cwidth;
1229  $size[1] = $cheight;
1230 
1231  // Breite und Höhe dürfen nicht größer sein als der Ausschnitt
1232  if ($width > $cwidth) {
1233  $width = $cwidth;
1234  } elseif (!$width) {
1235  $width = (int)round($cwidth * ($height / $cheight));
1236  }
1237  if ($height > $cheight) {
1238  $height = $cheight;
1239  } elseif (!$height) {
1240  $height = (int)round($cheight * ($width / $cwidth));
1241  }
1242  }
1243 
1244  // Bild in Graustufen
1245  if ($modify['grayscale']) {
1246  $imageTransform->grayscale();
1247  }
1248  }
1249 
1250  if ($_REQUEST['max']) {
1251  if ($width) {
1252  // Breite ändern, Seitenverhältnis beibehalten
1253  $w = $width;
1254  $h = (int)round($size[1] * ($width / $size[0]));
1255  }
1256  if ($height && $h > $height) {
1257  // Höhe ändern, Seitenverhältnis beibehalten
1258  $w = (int)round($w * ($height / $h));
1259  $h = $height;
1260  }
1261  $imageTransform->resize($w, $h);
1262  } else {
1263  if ($width && $height) {
1264  // Höhe und Breite ändern
1265  $imageTransform->resize($width, $height);
1266  } else if ($width) {
1267  // Breite ändern, Seitenverhältnis beibehalten
1268  $imageTransform->scaleByX($width);
1269  } else if ($height) {
1270  // Höhe ändern, Seitenverhältnis beibehalten
1271  $imageTransform->scaleByY($height);
1272  }
1273  }
1274 
1275  // Hook für eigene Transformationen anwenden
1276  if ($output_transform) {
1277  $this->page->outputTransform($imageTransform, $tmp, $width, $height, $modify);
1278  }
1279 
1280  // Wasserzeichen einfügen
1281  if ($output_watermark) {
1282  $imageTransform->watermark($output_watermark);
1283  }
1284 
1285  if (
1286  !$this->error
1287  && $imageTransform->save($file, ($this->web_format ? '' : 'jpeg'), $quality)
1288  ) {
1289  $imageTransform->free();
1290  $this->file = $file;
1291  }
1292  }
1293  @unlink($tmp);
1294  }
1295  } else {
1296  return false;
1297  }
1298  }
1299  }
1300  return true;
1301  }
1302 
1309  public function setFile($file) {
1310  $this->file = $file;
1311  $this->detectMimeType();
1312  $this->is_valid = true;
1313  }
1314 
1320  public function removeFile() {
1321  @unlink($this->file);
1322  $this->file = '';
1323  $this->is_valid = false;
1324  }
1325 
1333  public function setCache($cache, $etag = '') {
1334  $this->cache = $cache;
1335  $this->etag = $etag;
1336  }
1337 
1344  public function setTemporary($temporary) {
1345  $this->temporary = $temporary;
1346  }
1347 
1355  public function createArchive($path, $type = 'tar') {
1356  if (@is_dir($path)) {
1357  // Archiv erstellen
1358  $dir = $GLOBALS['egotec_conf']['tmp_dir'];
1359  $name = basename($path);
1360 
1361  $cwd = getcwd();
1362  chdir($dir);
1363  switch ($type) {
1364  case 'zip':
1365  $file = $name.'.zip';
1366  require_once 'Archive/Zip.php';
1367  $zip = new Archive_Zip($file);
1368  $files = [];
1369 
1370  foreach (scandir($path) as $entry) {
1371  if (!in_array($entry, ['.', '..'])) {
1372  $files[] = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $entry;
1373  }
1374  }
1375 
1376  $zip->add($files, ['remove_all_path' => true]);
1377  break;
1378 
1379  case 'tar':
1380  default:
1381  $file = $name.'.tar.gz';
1382  require_once 'Archive/Tar.php';
1383  $zip = new Archive_Tar($file, 'gz');
1384  $zip->_separator = ',';
1385  $zip->setIgnoreList(array($dir));
1386  $zip->createModify(rtrim($path, DIRECTORY_SEPARATOR), '', $name);
1387  }
1388  chdir($cwd);
1389 
1390  return $dir.$file;
1391  }
1392  return null;
1393  }
1394 
1402  public static function export($key, $type) {
1403  $source = '';
1404  switch ($type) {
1405  case 'workflow':
1406  require_once 'workflow/Ego_Workflow.php';
1407  $workflow = new Ego_Workflow($key);
1408  $source = $workflow->export();
1409  break;
1410  case 'template':
1411  require_once 'base/template/Ego_Template.php';
1412  $template = new Ego_Template($key);
1413  $source = $template->export();
1414  }
1415  if (!empty($source)) {
1416  $output = new Ego_Output($source);
1417  $output->setTemporary(true);
1418  $output->download();
1419  }
1420  }
1421 
1432  private function getThumbnail($width, $height = 0, $pool = '', $dir = '', $params = []) {
1433  if ($this->page) {
1434  return $this->page->getThumbnail($width, $height, $pool, $dir, $params);
1435  } else {
1436  // TODO Thumbnail für Dateien, die keinem Page Objekt zugeordnet sind
1437  }
1438  return null;
1439  }
1440 
1446  public function getFile() {
1447  return $this->file;
1448  }
1449 
1456  private function getFilesize($file) {
1457  return (int) @filesize($file);
1458  }
1459 }
1460 ?>
static getDimensions($file)
Definition: Ego_Image.php:626
setName($name)
Definition: Ego_Output.php:685
createArchive($path, $type='tar')
convertImage($format, $reset=false)
Definition: Ego_Output.php:436
error($image=false, $code=404)
Definition: Ego_Output.php:576
static export($key, $type)
setFile($file)
setTemporary($temporary)
clearConvertedImages($format='')
Definition: Ego_Output.php:512
__construct($source=null, $pool='', $dir='')
Definition: Ego_Output.php:178
transform($width, $height, $modify=array())
pdf($name='', $temporary=true, $cookie=true, $return=1)
static $convert_types
Definition: Ego_Output.php:146
read($download=false)
Definition: Ego_Output.php:894
getConvertedImages()
Definition: Ego_Output.php:532
setCache($cache, $etag='')
static exec(String $command, Array $params=array(), &$output=null, &$return_var=null, $log=true)
static file_put_contents($filename, $data, $flags=0, $context=null)
static getFormats($type)
static header($header, $replace=true)
static dateEncode($string)
Definition: Ego_System.php:651
static isDevMode($ignore=true)
static file_exists($file)
static mkdir($dir, $mode=0755, $recursive=true)
Definition: Ego_System.php:669
static file_get_contents($filename, $utf8=true, $context=null)
const VIDEO_RESOLUTIONS
Definition: Ego_System.php:33
static noCache()
static copy($src, $dest, $except='', $useLinks=false, $noArchive=false, $preserveDate=false)
static byte_format($byte)
Definition: Site.php:30