| 
									
										
										
										
											2014-05-18 12:57:46 +02:00
										 |  |  | <?php | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * StatusNet, the distributed open-source microblogging tool | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * widget for displaying a list of notice attachments | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * PHP version 5 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * LICENCE: This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU Affero General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @category  UI | 
					
						
							|  |  |  |  * @package   StatusNet | 
					
						
							|  |  |  |  * @author    Evan Prodromou <evan@status.net> | 
					
						
							|  |  |  |  * @author    Sarven Capadisli <csarven@status.net> | 
					
						
							|  |  |  |  * @copyright 2008 StatusNet, Inc. | 
					
						
							|  |  |  |  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 | 
					
						
							|  |  |  |  * @link      http://status.net/ | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (!defined('GNUSOCIAL')) { exit(1); } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * widget for displaying a single notice | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This widget has the core smarts for showing a single notice: what to display, | 
					
						
							|  |  |  |  * where, and under which circumstances. Its key method is show(); this is a recipe | 
					
						
							|  |  |  |  * that calls all the other show*() methods to build up a single notice. The | 
					
						
							|  |  |  |  * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip | 
					
						
							|  |  |  |  * author info (since that's implicit by the data in the page). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @category UI | 
					
						
							|  |  |  |  * @package  StatusNet | 
					
						
							|  |  |  |  * @author   Evan Prodromou <evan@status.net> | 
					
						
							|  |  |  |  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 | 
					
						
							|  |  |  |  * @link     http://status.net/ | 
					
						
							|  |  |  |  * @see      NoticeList | 
					
						
							|  |  |  |  * @see      ProfileNoticeListItem | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class AttachmentListItem extends Widget | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /** The attachment this item will show. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var $attachment = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @param File $attachment the attachment we will display | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function __construct(File $attachment, $out=null) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         parent::__construct($out); | 
					
						
							|  |  |  |         $this->attachment  = $attachment; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function title() { | 
					
						
							| 
									
										
										
										
											2016-07-07 00:45:31 +02:00
										 |  |  |         return $this->attachment->getTitle() ?: _('Untitled attachment'); | 
					
						
							| 
									
										
										
										
											2014-05-18 12:57:46 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function linkTitle() { | 
					
						
							|  |  |  |         return $this->title(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * recipe function for displaying a single notice. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * This uses all the other methods to correctly display a notice. Override | 
					
						
							|  |  |  |      * it or one of the others to fine-tune the output. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return void | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function show() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->showStart(); | 
					
						
							|  |  |  |         $this->showNoticeAttachment(); | 
					
						
							|  |  |  |         $this->showEnd(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function linkAttr() { | 
					
						
							|  |  |  |         return array('class' => 'attachment', | 
					
						
							| 
									
										
										
										
											2016-05-01 11:26:28 +02:00
										 |  |  |                      'href' => $this->attachment->getAttachmentUrl(), | 
					
						
							|  |  |  |                      'id' => 'attachment-' . $this->attachment->getID(), | 
					
						
							| 
									
										
										
										
											2014-05-18 12:57:46 +02:00
										 |  |  |                      'title' => $this->linkTitle()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function showLink() { | 
					
						
							|  |  |  |         $this->out->elementStart('a', $this->linkAttr()); | 
					
						
							|  |  |  |         $this->out->element('span', null, $this->linkTitle()); | 
					
						
							|  |  |  |         $this->showRepresentation(); | 
					
						
							|  |  |  |         $this->out->elementEnd('a'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function showNoticeAttachment() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->showLink(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function showRepresentation() { | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |         $enclosure = $this->attachment->getEnclosure(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |         if (Event::handle('StartShowAttachmentRepresentation', array($this->out, $this->attachment))) { | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |             if (!empty($enclosure->mimetype)) { | 
					
						
							|  |  |  |                 // First, prepare a thumbnail if it exists.
 | 
					
						
							|  |  |  |                 $thumb = null; | 
					
						
							|  |  |  |                 try { | 
					
						
							|  |  |  |                     // Tell getThumbnail that we can show an animated image if it has one (4th arg, "force_still")
 | 
					
						
							|  |  |  |                     $thumb = $this->attachment->getThumbnail(null, null, false, false); | 
					
						
							|  |  |  |                 } catch (UseFileAsThumbnailException $e) { | 
					
						
							|  |  |  |                     $thumb = null; | 
					
						
							|  |  |  |                 } catch (UnsupportedMediaException $e) { | 
					
						
							|  |  |  |                     // FIXME: Show a good representation of unsupported/unshowable images
 | 
					
						
							|  |  |  |                     $thumb = null; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // Then get the kind of mediatype we're dealing with
 | 
					
						
							|  |  |  |                 $mediatype = common_get_mime_media($enclosure->mimetype); | 
					
						
							| 
									
										
										
										
											2015-04-05 21:35:54 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 // FIXME: Get proper mime recognition of Ogg files! If system has 'mediainfo', this should do it:
 | 
					
						
							|  |  |  |                 // $ mediainfo --inform='General;%InternetMediaType%'
 | 
					
						
							|  |  |  |                 if ($this->attachment->mimetype === 'application/ogg') { | 
					
						
							|  |  |  |                     $mediatype = 'video';   // because this element can handle Ogg/Vorbis etc. on its own
 | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2016-01-12 15:38:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 // Ugly hack to show text/html links which have a thumbnail (such as from oEmbed/OpenGraph image URLs)
 | 
					
						
							|  |  |  |                 if (!in_array($mediatype, ['image','audio','video']) && $thumb instanceof File_thumbnail) { | 
					
						
							|  |  |  |                     $mediatype = 'image'; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-05 11:59:31 +01:00
										 |  |  |                 switch ($mediatype) { | 
					
						
							|  |  |  |                 // Anything we understand as an image, if we need special treatment, do it in StartShowAttachmentRepresentation
 | 
					
						
							|  |  |  |                 case 'image': | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |                     if ($thumb instanceof File_thumbnail) { | 
					
						
							| 
									
										
										
										
											2016-01-01 14:13:16 +01:00
										 |  |  |                         $this->out->element('img', $thumb->getHtmlAttrs(['class'=>'u-photo', 'alt' => ''])); | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |                     } else { | 
					
						
							| 
									
										
										
										
											2016-07-07 00:45:31 +02:00
										 |  |  |                         try { | 
					
						
							|  |  |  |                             // getUrl(true) because we don't want to hotlink, could be made configurable
 | 
					
						
							|  |  |  |                             $this->out->element('img', ['class'=>'u-photo', 'src'=>$this->attachment->getUrl(true), 'alt' => $this->attachment->getTitle()]); | 
					
						
							|  |  |  |                         } catch (FileNotStoredLocallyException $e) { | 
					
						
							|  |  |  |                             $url = $e->file->getUrl(false); | 
					
						
							|  |  |  |                             $this->out->element('a', ['href'=>$url, 'rel'=>'external'], $url); | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |                     unset($thumb);  // there's no need carrying this along after this
 | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                     break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-05 11:59:31 +01:00
										 |  |  |                 // HTML5 media elements
 | 
					
						
							|  |  |  |                 case 'audio': | 
					
						
							|  |  |  |                 case 'video': | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |                     if ($thumb instanceof File_thumbnail) { | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                         $poster = $thumb->getUrl(); | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |                         unset($thumb);  // there's no need carrying this along after this
 | 
					
						
							| 
									
										
										
										
											2016-01-09 13:15:09 +01:00
										 |  |  |                     } else { | 
					
						
							|  |  |  |                         $poster = null; | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                     $this->out->elementStart($mediatype, | 
					
						
							| 
									
										
										
										
											2014-06-21 21:01:17 +02:00
										 |  |  |                                         array('class'=>"attachment_player u-{$mediatype}", | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                                             'poster'=>$poster, | 
					
						
							|  |  |  |                                             'controls'=>'controls')); | 
					
						
							|  |  |  |                     $this->out->element('source', | 
					
						
							| 
									
										
										
										
											2015-03-05 11:59:31 +01:00
										 |  |  |                                         array('src'=>$this->attachment->getUrl(), | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                                             'type'=>$this->attachment->mimetype)); | 
					
						
							|  |  |  |                     $this->out->elementEnd($mediatype); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-05 11:59:31 +01:00
										 |  |  |                 default: | 
					
						
							| 
									
										
										
										
											2016-01-07 17:35:37 +01:00
										 |  |  |                     unset($thumb);  // there's no need carrying this along
 | 
					
						
							| 
									
										
										
										
											2016-07-21 01:38:31 +02:00
										 |  |  |                     switch (common_bare_mime($this->attachment->mimetype)) { | 
					
						
							| 
									
										
										
										
											2016-01-25 16:54:40 +01:00
										 |  |  |                     case 'text/plain': | 
					
						
							|  |  |  |                         $this->element('div', ['class'=>'e-content plaintext'], file_get_contents($this->attachment->getPath())); | 
					
						
							| 
									
										
										
										
											2016-02-03 19:32:51 +01:00
										 |  |  |                         break; | 
					
						
							| 
									
										
										
										
											2015-03-05 11:59:31 +01:00
										 |  |  |                     case 'text/html': | 
					
						
							|  |  |  |                         if (!empty($this->attachment->filename) | 
					
						
							|  |  |  |                                 && (GNUsocial::isAjax() || common_config('attachments', 'show_html'))) { | 
					
						
							|  |  |  |                             // Locally-uploaded HTML. Scrub and display inline.
 | 
					
						
							|  |  |  |                             $this->showHtmlFile($this->attachment); | 
					
						
							|  |  |  |                             break; | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         // Fall through to default if it wasn't a _local_ text/html File object
 | 
					
						
							|  |  |  |                     default: | 
					
						
							|  |  |  |                         Event::handle('ShowUnsupportedAttachmentRepresentation', array($this->out, $this->attachment)); | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 Event::handle('ShowUnsupportedAttachmentRepresentation', array($this->out, $this->attachment)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         Event::handle('EndShowAttachmentRepresentation', array($this->out, $this->attachment)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected function showHtmlFile(File $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $body = $this->scrubHtmlFile($attachment); | 
					
						
							|  |  |  |         if ($body) { | 
					
						
							|  |  |  |             $this->out->raw($body); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @return mixed false on failure, HTML fragment string on success | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function scrubHtmlFile(File $attachment) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-03-07 22:33:34 +01:00
										 |  |  |         $path = $attachment->getPath(); | 
					
						
							| 
									
										
										
										
											2014-05-18 14:05:29 +02:00
										 |  |  |         $raw = file_get_contents($path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Normalize...
 | 
					
						
							|  |  |  |         $dom = new DOMDocument(); | 
					
						
							|  |  |  |         if(!$dom->loadHTML($raw)) { | 
					
						
							|  |  |  |             common_log(LOG_ERR, "Bad HTML in local HTML attachment $path"); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Remove <script>s or htmlawed will dump their contents into output!
 | 
					
						
							|  |  |  |         // Note: removing child nodes while iterating seems to mess things up,
 | 
					
						
							|  |  |  |         // hence the double loop.
 | 
					
						
							|  |  |  |         $scripts = array(); | 
					
						
							|  |  |  |         foreach ($dom->getElementsByTagName('script') as $script) { | 
					
						
							|  |  |  |             $scripts[] = $script; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         foreach ($scripts as $script) { | 
					
						
							|  |  |  |             common_log(LOG_DEBUG, $script->textContent); | 
					
						
							|  |  |  |             $script->parentNode->removeChild($script); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Trim out everything outside the body...
 | 
					
						
							|  |  |  |         $body = $dom->saveHTML(); | 
					
						
							|  |  |  |         $body = preg_replace('/^.*<body[^>]*>/is', '', $body); | 
					
						
							|  |  |  |         $body = preg_replace('/<\/body[^>]*>.*$/is', '', $body); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-28 18:57:36 +01:00
										 |  |  |         require_once INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'; | 
					
						
							|  |  |  |         $purifier = new HTMLPurifier(); | 
					
						
							|  |  |  |         return $purifier->purify($body); | 
					
						
							| 
									
										
										
										
											2014-05-18 12:57:46 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * start a single notice. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return void | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function showStart() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // XXX: RDFa
 | 
					
						
							|  |  |  |         // TODO: add notice_type class e.g., notice_video, notice_image
 | 
					
						
							|  |  |  |         $this->out->elementStart('li'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * finish the notice | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Close the last elements in the notice list item | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return void | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function showEnd() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->out->elementEnd('li'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |