/** * Render core video thumbnail HTML * @requestParam MediaTransformOutput thumb * @requestParam array options - See ThumbnailHelper class for more detail * @responseParam string width * @responseParam string height * @responseParam string linkHref * @responseParam array linkClasses * @responseParam string linkId * @responseParam array linkAttrs * @responseParam string imgSrc * @responseParam string mediaKey * @responseParam string mediaName * @responseParam array imgClass * @responseParam array extraImgAttrs * @responseParam string dataSrc - data-src attribute for image lazy loading * @responseParam string duration (HH:MM:SS) * @responseParam array durationISO * @responseParam string mediaType - 'image' | 'video' */ public function video() { wfProfileIn(__METHOD__); $thumb = $this->getVal('thumb'); $options = $this->getVal('options', []); ThumbnailHelper::setVideoLinkClasses($this, $thumb, $options); ThumbnailHelper::setVideoLinkAttribs($this, $thumb, $options); ThumbnailHelper::setVideoImgAttribs($this, $thumb, $options); ThumbnailHelper::setExtraImgAttribs($this, $options); ThumbnailHelper::setExtraLinkAttribs($this, $options); // Set duration // The file is not always an instance of a class with magic getters implemented. see VID-1753 $file = $thumb->file; if (is_callable([$file, 'getMetadataDuration'])) { $duration = $file->getMetadataDuration(); } else { $duration = null; } $this->response->setVal('duration', WikiaFileHelper::formatDuration($duration)); $this->response->setVal('mediaType', 'video'); $lazyLoaded = ThumbnailHelper::setLazyLoad($this, $options); if (!$lazyLoaded) { // Only add RDF metadata when the thumb is not lazy loaded $this->response->setVal('rdf', true); if (!empty($duration)) { $this->response->setVal('durationISO', WikiaFileHelper::formatDurationISO8601($duration)); } } wfProfileOut(__METHOD__); }
/** * @dataProvider durationDataProvider */ public function testDuration($sec, $expResult) { // setup $this->setUpMock(); // test $responseData = WikiaFileHelper::formatDuration($sec); $this->assertEquals($expResult, $responseData); }
/** * Numbers given by Exif user agents are often magical, that is they * should be replaced by a detailed explanation depending on their * value which most of the time are plain integers. This function * formats Exif (and other metadata) values into human readable form. * * @param $tags Array: the Exif data to format ( as returned by * Exif::getFilteredData() or BitmapMetadataHandler ) * @return array */ public static function getFormattedData($tags) { global $wgLang; $resolutionunit = !isset($tags['ResolutionUnit']) || $tags['ResolutionUnit'] == 2 ? 2 : 3; unset($tags['ResolutionUnit']); foreach ($tags as $tag => &$vals) { // This seems ugly to wrap non-array's in an array just to unwrap again, // especially when most of the time it is not an array if (!is_array($tags[$tag])) { $vals = array($vals); } // _type is a special value to say what array type if (isset($tags[$tag]['_type'])) { $type = $tags[$tag]['_type']; unset($vals['_type']); } else { $type = 'ul'; // default unordered list. } //This is done differently as the tag is an array. if ($tag == 'GPSTimeStamp' && count($vals) === 3) { //hour min sec array $h = explode('/', $vals[0]); $m = explode('/', $vals[1]); $s = explode('/', $vals[2]); // this should already be validated // when loaded from file, but it could // come from a foreign repo, so be // paranoid. if (!isset($h[1]) || !isset($m[1]) || !isset($s[1]) || $h[1] == 0 || $m[1] == 0 || $s[1] == 0) { continue; } $tags[$tag] = intval($h[0] / $h[1]) . ':' . str_pad(intval($m[0] / $m[1]), 2, '0', STR_PAD_LEFT) . ':' . str_pad(intval($s[0] / $s[1]), 2, '0', STR_PAD_LEFT); $time = wfTimestamp(TS_MW, '1971:01:01 ' . $tags[$tag]); // the 1971:01:01 is just a placeholder, and not shown to user. if ($time && intval($time) > 0) { $tags[$tag] = $wgLang->time($time); } continue; } // The contact info is a multi-valued field // instead of the other props which are single // valued (mostly) so handle as a special case. if ($tag === 'Contact') { $vals = self::collapseContactInfo($vals); continue; } foreach ($vals as &$val) { switch ($tag) { case 'Compression': switch ($val) { case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 32773: case 32946: case 34712: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'PhotometricInterpretation': switch ($val) { case 2: case 6: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'Orientation': switch ($val) { case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'PlanarConfiguration': switch ($val) { case 1: case 2: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; // TODO: YCbCrSubSampling // TODO: YCbCrSubSampling case 'YCbCrPositioning': switch ($val) { case 1: case 2: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'XResolution': case 'YResolution': switch ($resolutionunit) { case 2: $val = self::msg('XYResolution', 'i', self::formatNum($val)); break; case 3: $val = self::msg('XYResolution', 'c', self::formatNum($val)); break; default: /* If not recognized, display as is. */ break; } break; // TODO: YCbCrCoefficients #p27 (see annex E) // TODO: YCbCrCoefficients #p27 (see annex E) case 'ExifVersion': case 'FlashpixVersion': $val = "{$val}" / 100; break; case 'ColorSpace': switch ($val) { case 1: case 65535: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'ComponentsConfiguration': switch ($val) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'DateTime': case 'DateTimeOriginal': case 'DateTimeDigitized': case 'DateTimeReleased': case 'DateTimeExpires': case 'GPSDateStamp': case 'dc-date': case 'DateTimeMetadata': if ($val == '0000:00:00 00:00:00' || $val == ' : : : : ') { $val = wfMsg('exif-unknowndate'); } elseif (preg_match('/^(?:\\d{4}):(?:\\d\\d):(?:\\d\\d) (?:\\d\\d):(?:\\d\\d):(?:\\d\\d)$/D', $val)) { // Full date. $time = wfTimestamp(TS_MW, $val); if ($time && intval($time) > 0) { $val = $wgLang->timeanddate($time); } } elseif (preg_match('/^(?:\\d{4}):(?:\\d\\d):(?:\\d\\d) (?:\\d\\d):(?:\\d\\d)$/D', $val)) { // No second field. Still format the same // since timeanddate doesn't include seconds anyways, // but second still available in api $time = wfTimestamp(TS_MW, $val . ':00'); if ($time && intval($time) > 0) { $val = $wgLang->timeanddate($time); } } elseif (preg_match('/^(?:\\d{4}):(?:\\d\\d):(?:\\d\\d)$/D', $val)) { // If only the date but not the time is filled in. $time = wfTimestamp(TS_MW, substr($val, 0, 4) . substr($val, 5, 2) . substr($val, 8, 2) . '000000'); if ($time && intval($time) > 0) { $val = $wgLang->date($time); } } // else it will just output $val without formatting it. break; /** Wikia change start */ /** Wikia change start */ case 'duration': $val = WikiaFileHelper::formatDuration($val); break; case 'published': global $wgContLang; $val = $wgContLang->date($val); break; /** Wikia change end */ /** Wikia change end */ case 'ExposureProgram': switch ($val) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'SubjectDistance': $val = self::msg($tag, '', self::formatNum($val)); break; case 'MeteringMode': switch ($val) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 255: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'LightSource': switch ($val) { case 0: case 1: case 2: case 3: case 4: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 255: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'Flash': $flashDecode = array('fired' => $val & bindec('00000001'), 'return' => ($val & bindec('00000110')) >> 1, 'mode' => ($val & bindec('00011000')) >> 3, 'function' => ($val & bindec('00100000')) >> 5, 'redeye' => ($val & bindec('01000000')) >> 6); # We do not need to handle unknown values since all are used. foreach ($flashDecode as $subTag => $subValue) { # We do not need any message for zeroed values. if ($subTag != 'fired' && $subValue == 0) { continue; } $fullTag = $tag . '-' . $subTag; $flashMsgs[] = self::msg($fullTag, $subValue); } $val = $wgLang->commaList($flashMsgs); break; case 'FocalPlaneResolutionUnit': switch ($val) { case 2: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'SensingMethod': switch ($val) { case 1: case 2: case 3: case 4: case 5: case 7: case 8: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'FileSource': switch ($val) { case 3: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'SceneType': switch ($val) { case 1: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'CustomRendered': switch ($val) { case 0: case 1: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'ExposureMode': switch ($val) { case 0: case 1: case 2: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'WhiteBalance': switch ($val) { case 0: case 1: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'SceneCaptureType': switch ($val) { case 0: case 1: case 2: case 3: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GainControl': switch ($val) { case 0: case 1: case 2: case 3: case 4: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'Contrast': switch ($val) { case 0: case 1: case 2: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'Saturation': switch ($val) { case 0: case 1: case 2: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'Sharpness': switch ($val) { case 0: case 1: case 2: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'SubjectDistanceRange': switch ($val) { case 0: case 1: case 2: case 3: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; //The GPS...Ref values are kept for compatibility, probably won't be reached. //The GPS...Ref values are kept for compatibility, probably won't be reached. case 'GPSLatitudeRef': case 'GPSDestLatitudeRef': switch ($val) { case 'N': case 'S': $val = self::msg('GPSLatitude', $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GPSLongitudeRef': case 'GPSDestLongitudeRef': switch ($val) { case 'E': case 'W': $val = self::msg('GPSLongitude', $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GPSAltitude': if ($val < 0) { $val = self::msg('GPSAltitude', 'below-sealevel', self::formatNum(-$val, 3)); } else { $val = self::msg('GPSAltitude', 'above-sealevel', self::formatNum($val, 3)); } break; case 'GPSStatus': switch ($val) { case 'A': case 'V': $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GPSMeasureMode': switch ($val) { case 2: case 3: $val = self::msg($tag, $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GPSTrackRef': case 'GPSImgDirectionRef': case 'GPSDestBearingRef': switch ($val) { case 'T': case 'M': $val = self::msg('GPSDirection', $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GPSLatitude': case 'GPSDestLatitude': $val = self::formatCoords($val, 'latitude'); break; case 'GPSLongitude': case 'GPSDestLongitude': $val = self::formatCoords($val, 'longitude'); break; case 'GPSSpeedRef': switch ($val) { case 'K': case 'M': case 'N': $val = self::msg('GPSSpeed', $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GPSDestDistanceRef': switch ($val) { case 'K': case 'M': case 'N': $val = self::msg('GPSDestDistance', $val); break; default: /* If not recognized, display as is. */ break; } break; case 'GPSDOP': // See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS) if ($val <= 2) { $val = self::msg($tag, 'excellent', self::formatNum($val)); } elseif ($val <= 5) { $val = self::msg($tag, 'good', self::formatNum($val)); } elseif ($val <= 10) { $val = self::msg($tag, 'moderate', self::formatNum($val)); } elseif ($val <= 20) { $val = self::msg($tag, 'fair', self::formatNum($val)); } else { $val = self::msg($tag, 'poor', self::formatNum($val)); } break; // This is not in the Exif standard, just a special // case for our purposes which enables wikis to wikify // the make, model and software name to link to their articles. // This is not in the Exif standard, just a special // case for our purposes which enables wikis to wikify // the make, model and software name to link to their articles. case 'Make': case 'Model': $val = self::msg($tag, '', $val); break; case 'Software': if (is_array($val)) { //if its a software, version array. $val = wfMsg('exif-software-version-value', $val[0], $val[1]); } else { $val = self::msg($tag, '', $val); } break; case 'ExposureTime': // Show the pretty fraction as well as decimal version $val = wfMsg('exif-exposuretime-format', self::formatFraction($val), self::formatNum($val)); break; case 'ISOSpeedRatings': // If its = 65535 that means its at the // limit of the size of Exif::short and // is really higher. if ($val == '65535') { $val = self::msg($tag, 'overflow'); } else { $val = self::formatNum($val); } break; case 'FNumber': $val = wfMsg('exif-fnumber-format', self::formatNum($val)); break; case 'FocalLength': case 'FocalLengthIn35mmFilm': $val = wfMsg('exif-focallength-format', self::formatNum($val)); break; case 'MaxApertureValue': if (strpos($val, '/') !== false) { // need to expand this earlier to calculate fNumber list($n, $d) = explode('/', $val); if (is_numeric($n) && is_numeric($d)) { $val = $n / $d; } } if (is_numeric($val)) { $fNumber = pow(2, $val / 2); if ($fNumber !== false) { $val = wfMsg('exif-maxaperturevalue-value', self::formatNum($val), self::formatNum($fNumber, 2)); } } break; case 'iimCategory': switch (strtolower($val)) { // See pg 29 of IPTC photo // metadata standard. case 'ace': case 'clj': case 'dis': case 'fin': case 'edu': case 'evn': case 'hth': case 'hum': case 'lab': case 'lif': case 'pol': case 'rel': case 'sci': case 'soi': case 'spo': case 'war': case 'wea': $val = self::msg('iimcategory', $val); } break; case 'SubjectNewsCode': // Essentially like iimCategory. // 8 (numeric) digit hierarchical // classification. We decode the // first 2 digits, which provide // a broad category. $val = self::convertNewsCode($val); break; case 'Urgency': // 1-8 with 1 being highest, 5 normal // 0 is reserved, and 9 is 'user-defined'. $urgency = ''; if ($val == 0 || $val == 9) { $urgency = 'other'; } elseif ($val < 5 && $val > 1) { $urgency = 'high'; } elseif ($val == 5) { $urgency = 'normal'; } elseif ($val <= 8 && $val > 5) { $urgency = 'low'; } if ($urgency !== '') { $val = self::msg('urgency', $urgency, $val); } break; // Things that have a unit of pixels. // Things that have a unit of pixels. case 'OriginalImageHeight': case 'OriginalImageWidth': case 'PixelXDimension': case 'PixelYDimension': case 'ImageWidth': case 'ImageLength': $val = self::formatNum($val) . ' ' . wfMsg('unit-pixel'); break; // Do not transform fields with pure text. // For some languages the formatNum() // conversion results to wrong output like // foo,bar@example,com or foo٫bar@example٫com. // Also some 'numeric' things like Scene codes // are included here as we really don't want // commas inserted. // Do not transform fields with pure text. // For some languages the formatNum() // conversion results to wrong output like // foo,bar@example,com or foo٫bar@example٫com. // Also some 'numeric' things like Scene codes // are included here as we really don't want // commas inserted. case 'ImageDescription': case 'Artist': case 'Copyright': case 'RelatedSoundFile': case 'ImageUniqueID': case 'SpectralSensitivity': case 'GPSSatellites': case 'GPSVersionID': case 'GPSMapDatum': case 'Keywords': case 'WorldRegionDest': case 'CountryDest': case 'CountryCodeDest': case 'ProvinceOrStateDest': case 'CityDest': case 'SublocationDest': case 'WorldRegionCreated': case 'CountryCreated': case 'CountryCodeCreated': case 'ProvinceOrStateCreated': case 'CityCreated': case 'SublocationCreated': case 'ObjectName': case 'SpecialInstructions': case 'Headline': case 'Credit': case 'Source': case 'EditStatus': case 'FixtureIdentifier': case 'LocationDest': case 'LocationDestCode': case 'Writer': case 'JPEGFileComment': case 'iimSupplementalCategory': case 'OriginalTransmissionRef': case 'Identifier': case 'dc-contributor': case 'dc-coverage': case 'dc-publisher': case 'dc-relation': case 'dc-rights': case 'dc-source': case 'dc-type': case 'Lens': case 'SerialNumber': case 'CameraOwnerName': case 'Label': case 'Nickname': case 'RightsCertificate': case 'CopyrightOwner': case 'UsageTerms': case 'WebStatement': case 'OriginalDocumentID': case 'LicenseUrl': case 'MorePermissionsUrl': case 'AttributionUrl': case 'PreferredAttributionName': case 'PNGFileComment': case 'Disclaimer': case 'ContentWarning': case 'GIFFileComment': case 'SceneCode': case 'IntellectualGenre': case 'Event': case 'OrginisationInImage': case 'PersonInImage': $val = htmlspecialchars($val); break; case 'ObjectCycle': switch ($val) { case 'a': case 'p': case 'b': $val = self::msg($tag, $val); break; default: $val = htmlspecialchars($val); break; } break; case 'Copyrighted': switch ($val) { case 'True': case 'False': $val = self::msg($tag, $val); break; } break; case 'Rating': if ($val == '-1') { $val = self::msg($tag, 'rejected'); } else { $val = self::formatNum($val); } break; case 'LanguageCode': $lang = $wgLang->getLanguageName(strtolower($val)); if ($lang) { $val = htmlspecialchars($lang); } else { $val = htmlspecialchars($val); } break; default: $val = self::formatNum($val); break; } } // End formatting values, start flattening arrays. $vals = self::flattenArray($vals, $type); } return $tags; }
/** * Return a HTML representation of the image gallery * * The new gallery disables the old perrow control, and automatically fit the gallery to the available space in the browser. */ private function renderGallery() { wfProfileIn(__METHOD__); // do not render empty gallery if (empty($this->mFiles)) { wfProfileOut(__METHOD__); return ''; } // Route to the mobile gallery or the new MediaGallery if (F::app()->checkSkin('wikiamobile')) { $html = $this->renderWikiaMobileMediaGroup(); wfProfileOut(__METHOD__); return $html; } elseif ($this->canRenderMediaGallery()) { $html = $this->renderMediaGallery(); // remove spaces from html produced by mustache template $html = trim(preg_replace('/\\n+/', ' ', $html)); wfProfileOut(__METHOD__); return $html; } /** @var Skin|Linker $skin The skin object falls back to Linker methods via __call */ $skin = RequestContext::getMain()->getSkin(); $thumbSize = $this->mWidths; $orientation = $this->getParam('orientation'); $ratio = WikiaPhotoGalleryHelper::getRatioFromOption($orientation); $crop = $this->mCrop; //calculate height of the biggest image $maxHeight = 0; $fileObjectsCache = array(); $heights = array(); $widths = array(); $thumbParams = array(); // loop through the images and get height of the tallest one foreach ($this->mFiles as $imageData) { $img = $this->getImage($imageData[0]); $fileObjectsCache[] = $img; if (!empty($img)) { // get thumbnail limited only by given width if ($img->width > $thumbSize) { $imageHeight = round($img->height * ($thumbSize / $img->width)); $imageWidth = $thumbSize; } else { $imageHeight = $img->height; $imageWidth = $img->width; } $heights[] = $imageHeight; $widths[] = $imageWidth; if ($imageHeight > $maxHeight) { $maxHeight = $imageHeight; } } } // calculate height based on gallery width $height = round($thumbSize / $ratio); if ($orientation == 'none') { $this->enableCropping($crop = false); // use the biggest height found if ($maxHeight > 0) { $height = $maxHeight; } // limit height (RT #59355) $height = min($height, $thumbSize); // recalculate dimensions (RT #59355) foreach ($this->mFiles as $index => $image) { if (!empty($heights[$index]) && !empty($widths[$index])) { //fix #59355, min() added to let borders wrap images with smaller width //fix #63886, round ( $tmpFloat ) != floor ( $tmpFloat ) added to check if thumbnail will be generated from proper width $tmpFloat = $widths[$index] * $height / $heights[$index]; $widths[$index] = min($widths[$index], floor($tmpFloat)); $heights[$index] = min($height, $heights[$index]); if (round($tmpFloat) != floor($tmpFloat)) { $heights[$index]--; } } else { $widths[$index] = $thumbSize; $heights[$index] = $height; } } } $useBuckets = $this->getParam('buckets'); $useRowDivider = $this->getParam('rowdivider'); $captionColor = $this->getParam('captiontextcolor'); $borderColor = $this->getParam('bordercolor'); $perRow = $this->mPerRow > 0 ? $this->mPerRow : 'dynamic'; $position = $this->getParam('position'); $captionsPosition = $this->getParam('captionposition', 'below'); $captionsAlign = $this->getParam('captionalign'); $captionsSize = $this->getParam('captionsize'); $captionsColor = !empty($captionColor) ? $captionColor : null; $spacing = $this->getParam('spacing'); $borderSize = $this->getParam('bordersize'); $borderColor = !empty($borderColor) ? $borderColor : 'accent'; $isTemplate = isset($this->mData['params']['source']) && $this->mData['params']['source'] == "template"; $hash = $this->mData['hash']; $id = 'gallery-' . $this->mData['id']; $showAddButton = $this->mShowAddButton == true; $hideOverflow = $this->getParam('hideoverflow'); if (in_array($borderColor, array('accent', 'color1'))) { $borderColorClass = " {$borderColor}"; } else { $borderColorCSS = " border-color: {$borderColor};"; if ($captionsPosition == 'within') { $captionsBackgroundColor = $borderColor; } } $html = Xml::openElement('div', array('id' => $id, 'hash' => $hash, 'class' => 'wikia-gallery' . ($isTemplate ? ' template' : null) . " wikia-gallery-caption-{$captionsPosition}" . " wikia-gallery-position-{$position}" . " wikia-gallery-spacing-{$spacing}" . " wikia-gallery-border-{$borderSize}" . " wikia-gallery-captions-{$captionsAlign}" . " wikia-gallery-caption-size-{$captionsSize}")); // render gallery caption (RT #59241) if ($this->mCaption !== false) { $html .= Xml::openElement('div', array('class' => 'wikia-gallery-caption')) . $this->mCaption . Xml::closeElement('div'); } $itemWrapperWidth = $thumbSize; $thumbWrapperHeight = $height; //compensate image wrapper width depending on the border size switch ($borderSize) { case 'large': $itemWrapperWidth += 10; //5px * 2 $thumbWrapperHeight += 10; break; case 'medium': $itemWrapperWidth += 4; //2px * 2 $thumbWrapperHeight += 4; break; case 'small': $itemWrapperWidth += 2; //1px * 2 $thumbWrapperHeight += 2; break; } //adding more width for the padding $outeritemWrapperWidth = $itemWrapperWidth + 20; $rowDividerCSS = ''; if ($useRowDivider) { $rowDividerCSS = "height: " . ($thumbWrapperHeight + 100) . "px; padding: 30px 15px 20px 15px; margin: 0px; border-bottom: solid 1px #CCCCCC;"; } if ($useBuckets) { $itemSpanStyle = "width:{$outeritemWrapperWidth}px; " . ($useRowDivider ? $rowDividerCSS : 'margin: 4px;'); $itemDivStyle = "background-color: #f9f9f9; height:{$thumbWrapperHeight}px; text-align: center; border: solid 1px #CCCCCC; padding: " . ($outeritemWrapperWidth - $thumbWrapperHeight) / 2 . "px 5px;"; } else { $itemSpanStyle = "width:{$itemWrapperWidth}px; {$rowDividerCSS}"; $itemDivStyle = "height:{$thumbWrapperHeight}px;"; } foreach ($this->mFiles as $index => $imageData) { if ($perRow != 'dynamic' && $index % $perRow == 0) { $html .= Xml::openElement('div', array('class' => 'wikia-gallery-row')); } $html .= Xml::openElement('div', array('class' => 'wikia-gallery-item', 'style' => $itemSpanStyle)); $html .= Xml::openElement('div', array('class' => 'thumb', 'style' => $itemDivStyle)); $image = array(); // let's properly scale image (don't make it bigger than original size) /** * @var $imageTitle Title * @var $fileObject LocalFile */ $imageTitle = $imageData[0]; $fileObject = $fileObjectsCache[$index]; $imageTitleText = $imageTitle->getText(); $image['height'] = $height; $image['width'] = $thumbSize; $image['caption'] = $imageData[1]; if (!is_object($fileObject) || $imageTitle->getNamespace() != NS_FILE) { $image['linkTitle'] = $image['titleText'] = $imageTitleText; $image['thumbnail'] = false; $image['link'] = Skin::makeSpecialUrl("Upload", array('wpDestFile' => $image['linkTitle'])); $image['classes'] = 'image broken-image accent new'; } else { $thumbParams = WikiaPhotoGalleryHelper::getThumbnailDimensions($fileObject, $thumbSize, $height, $crop); $image['thumbnail'] = $fileObject->createThumb($thumbParams['width'], $thumbParams['height']); $image['DBKey'] = $fileObject->getTitle()->getDBKey(); $image['fileTitle'] = $fileObject->getTitle()->getText(); $image['height'] = $orientation == 'none' ? $heights[$index] : min($thumbParams['height'], $height); $imgHeightCompensation = ($height - $image['height']) / 2; if ($imgHeightCompensation > 0) { $image['heightCompensation'] = $imgHeightCompensation; } $image['width'] = min($widths[$index], $thumbSize); //Fix #59914, shared.css has auto-alignment rules /*$imgWidthCompensation = ($thumbSize - $image['width']) / 2; if ($imgHeightCompensation > 0) $image['widthCompensation'] = $imgWidthCompensation;*/ $image['link'] = $imageData[2]; $linkAttribs = $this->parseLink($imageTitle->getLocalUrl(), $imageTitleText, $image['link']); $image['link'] = $linkAttribs['href']; $image['linkTitle'] = $linkAttribs['title']; $image['classes'] = $linkAttribs['class']; $image['bytes'] = $fileObject->getSize(); if ($this->mParser && $fileObject->getHandler()) { $fileObject->getHandler()->parserTransformHook($this->mParser, $fileObject); } } wfRunHooks('GalleryBeforeRenderImage', array(&$image)); //see Image SEO project $wrapperId = preg_replace('/[^a-z0-9_]/i', '-', Sanitizer::escapeId($image['linkTitle'])); $html .= Xml::openElement('div', array('class' => 'gallery-image-wrapper' . (!$useBuckets && !empty($borderColorClass) ? $borderColorClass : null), 'id' => $wrapperId, 'style' => 'position: relative;' . ($useBuckets ? " width: {$itemWrapperWidth}px; border-style: none;" : " height:{$image['height']}px; width:{$image['width']}px;") . (!empty($image['heightCompensation']) ? " top:{$image['heightCompensation']}px;" : null) . (!empty($borderColorCSS) ? $borderColorCSS : null))); $imgStyle = null; $isVideo = WikiaFileHelper::isFileTypeVideo($fileObject); # Fix 59913 - thumbnail goes as <img /> not as <a> background. if ($orientation != 'none') { # margin calculation for image positioning if ($thumbParams['height'] > $image['height']) { $tempTopMargin = -1 * ($thumbParams['height'] - $image['height']) / 2; } else { unset($tempTopMargin); } if ($thumbParams['width'] > $image['width']) { $tempLeftMargin = -1 * ($thumbParams['width'] - $image['width']) / 2; } else { unset($tempLeftMargin); } $imgStyle = (!empty($tempTopMargin) ? " margin-top:" . $tempTopMargin . "px;" : null) . (!empty($tempLeftMargin) ? " margin-left:" . $tempLeftMargin . "px;" : null); if ($isVideo) { $image['classes'] .= ' force-lightbox'; } } $linkAttribs = array('class' => empty($image['thumbnail']) ? 'image-no-lightbox' : $image['classes'], 'href' => $image['link'], 'title' => $image['linkTitle'] . (isset($image['bytes']) ? ' (' . $skin->formatSize($image['bytes']) . ')' : ""), 'style' => "height:{$image['height']}px; width:{$image['width']}px;"); if (!empty($image['thumbnail'])) { if ($isVideo) { $thumbHtml = ''; $duration = $fileObject->getMetadataDuration(); if (!empty($duration)) { $duration = WikiaFileHelper::formatDuration($duration); $thumbHtml .= '<span class="duration">' . $duration . '</span>'; } $playButtonSize = ThumbnailHelper::getThumbnailSize($image['width']); $thumbHtml .= $this->videoPlayButton; $linkAttribs['class'] .= ' video video-thumbnail ' . $playButtonSize; } else { $thumbHtml = ''; } $imgAttribs = array('style' => (!empty($image['titleText']) ? " line-height:{$image['height']}px;" : null) . $imgStyle, 'src' => $image['thumbnail'] ? $image['thumbnail'] : null, 'title' => $image['linkTitle'] . (isset($image['bytes']) ? ' (' . $skin->formatSize($image['bytes']) . ')' : ""), 'class' => 'thumbimage', 'alt' => preg_replace('/\\.[^\\.]+$/', '', $image['linkTitle'])); if ($isVideo) { $imgAttribs['data-video-name'] = htmlspecialchars($image['fileTitle']); $imgAttribs['data-video-key'] = urlencode(htmlspecialchars($image['DBKey'])); } else { $imgAttribs['data-image-name'] = htmlspecialchars($image['fileTitle']); $imgAttribs['data-image-key'] = urlencode(htmlspecialchars($image['DBKey'])); } if (!empty($image['data-caption'])) { $imgAttribs['data-caption'] = $image['data-caption']; } if (isset($image['thumbnail-classes']) && isset($image['thumbnail-src']) && isset($image['thumbnail-onload'])) { $thumbHtml .= '<noscript>' . Xml::openElement('img', $imgAttribs) . '</noscript>'; $imgAttribs['class'] .= ' ' . $image['thumbnail-classes']; $imgAttribs['data-src'] = $imgAttribs['src']; $imgAttribs['src'] = $image['thumbnail-src']; $imgAttribs['onload'] = $image['thumbnail-onload']; } $thumbHtml .= Xml::openElement('img', $imgAttribs); } else { $thumbHtml = $image['linkTitle']; } $html .= Xml::openElement('a', $linkAttribs); $html .= $thumbHtml; $html .= Xml::closeElement('a'); if ($captionsPosition == 'below') { $html .= Xml::closeElement('div'); $html .= Xml::closeElement('div'); } // Insert video titles here if ($isVideo) { $html .= '<div class="title">' . $imageTitleText . '</div>'; } if (!empty($image['caption'])) { $html .= Xml::openElement('div', array('class' => 'lightbox-caption' . (!empty($borderColorClass) && $captionsPosition == 'within' ? $borderColorClass : null), 'style' => ($captionsPosition == 'below' ? "width:{$thumbSize}px;" : null) . (!empty($captionsColor) ? " color:{$captionsColor};" : null) . (!empty($captionsBackgroundColor) ? " background-color:{$captionsBackgroundColor}" : null) . ($useBuckets ? " margin-top: 0px;" : '') . (!empty($hideOverflow) ? " overflow: hidden" : null))); $html .= $image['caption']; $html .= Xml::closeElement('div'); } if ($captionsPosition == 'within') { $html .= Xml::closeElement('div'); $html .= Xml::closeElement('div'); } $html .= Xml::closeElement('div'); // /div.wikia-gallery-item if ($perRow != 'dynamic' && ($index % $perRow == $perRow - 1 || $index == count($this->mFiles) - 1)) { $html .= Xml::closeElement('div'); } } // "Add image to this gallery" button (this button is shown by JS only in Monaco) if ($showAddButton) { if ($perRow == 'dynamic') { $html .= Xml::element('br'); } // add button for Oasis $html .= Xml::openElement('a', array('class' => 'wikia-photogallery-add wikia-button noprint', 'style' => 'display: none')); $html .= Xml::element('img', array('src' => F::app()->wg->BlankImgUrl, 'class' => 'sprite photo', 'width' => 26, 'height' => 16)); $html .= wfMessage('wikiaPhotoGallery-viewmode-addphoto')->inContentLanguage()->text(); $html .= Xml::closeElement('a'); } $html .= Xml::closeElement('div'); wfProfileOut(__METHOD__); return $html; }
/** * get formatted duration * @return string */ public function getFormattedDuration() { $metadata = $this->getVideoMetadata(true); if (!empty($metadata['duration'])) { $sec = $metadata['duration']; if (is_numeric($sec)) { $formattedDuration = WikiaFileHelper::formatDuration($sec); return $formattedDuration; } else { return $metadata['duration']; } } return ''; }
/** * @param array $options * @return mixed|string * @throws MWException */ function toHtml($options = array()) { $app = F::app(); if (count(func_get_args()) == 2) { throw new MWException(__METHOD__ . ' called in the old style'); } // Check if the editor is requesting, if so, render image thumbnail instead if (!empty($app->wg->RTEParserEnabled)) { return $this->renderAsThumbnailImage($options); } wfProfileIn(__METHOD__); // All non-WikiaMobile skins use Nirvana to render HTML now. WikiaMobile is slowly migrating with 'useTemplate' if (!F::app()->checkSkin('wikiamobile') || !empty($options['useTemplate'])) { $html = $this->renderView($options); wfProfileOut(__METHOD__); return $html; } // Only WikiaMobile beyond this point WikiaLogger::instance()->debug('Media method ' . __METHOD__ . ' called', array_merge($options, ['url' => $this->url, 'method' => __METHOD__, 'page' => $this->page, 'mediaType' => $this->mediaType(), 'fileType' => get_class($this->file)])); $alt = empty($options['alt']) ? '' : $options['alt']; $videoTitle = $this->file->getTitle(); $useRDFData = !empty($options['disableRDF']) && $options['disableRDF'] == true ? false : true; $linkAttribs = array('href' => $videoTitle->getLocalURL()); if (!empty($options['id'])) { $linkAttribs['id'] = $options['id']; } if ($useRDFData) { // bugId: #46621 $linkAttribs['itemprop'] = 'video'; $linkAttribs['itemscope'] = ''; $linkAttribs['itemtype'] = 'http://schema.org/VideoObject'; } // let extension override any link attributes if (isset($options['linkAttribs']) && is_array($options['linkAttribs'])) { $linkAttribs = array_merge($linkAttribs, $options['linkAttribs']); } $extraClasses = 'video'; if (empty($options['noLightbox'])) { $extraClasses .= ' image lightbox'; } $linkAttribs['class'] = empty($linkAttribs['class']) ? $extraClasses : $linkAttribs['class'] . ' ' . $extraClasses; if (!empty($options['fixedHeight'])) { $this->height = $options['fixedHeight']; } $attribs = array('alt' => $alt, 'src' => empty($options['src']) ? $this->url : $options['src'], 'width' => $this->width, 'height' => $this->height, 'data-video-name' => htmlspecialchars($videoTitle->getText()), 'data-video-key' => htmlspecialchars(urlencode($videoTitle->getDBKey()))); if ($useRDFData) { $attribs['itemprop'] = 'thumbnail'; } // lazy loading if (!empty($options['usePreloading'])) { $attribs['data-src'] = $this->url; } // this is used for video thumbnails on file page history tables to insure you see the older version of a file when thumbnail is clicked. if ($this->file instanceof OldLocalFile) { $archive_name = $this->file->getArchiveName(); if (!empty($archive_name)) { $linkAttribs['href'] .= '?t=' . $this->file->getTimestamp(); $linkAttribs['data-timestamp'] = $this->file->getTimestamp(); } } if (!empty($options['valign'])) { $attribs['style'] = "vertical-align: {$options['valign']}"; } $attribs['class'] = 'Wikia-video-thumb'; if (!empty($options['img-class'])) { $attribs['class'] .= ' ' . $options['img-class']; } if (isset($options['imgExtraStyle'])) { if (!isset($attribs['style'])) { $attribs['style'] = ''; } $attribs['style'] .= $options['imgExtraStyle']; } if (isset($options['duration']) && $options['duration'] == true) { $duration = WikiaFileHelper::formatDuration($this->file->getMetadataDuration()); } if (!empty($duration)) { $timerProp = array('class' => 'timer'); if ($useRDFData) { $timerProp['itemprop'] = 'duration'; } } // WikiaMobile completely reconstructs the html $html = ''; //give extensions a chance to modify the markup wfRunHooks('ThumbnailVideoHTML', array($options, $linkAttribs, $attribs, $this->file, &$html)); wfProfileOut(__METHOD__); return $html; }
public function save() { if (!$this->wg->User->isAllowed('gameguidessponsored')) { $this->displayRestrictionError(); return false; // skip rendering } $this->response->setFormat('json'); $languages = $this->request->getArray('languages'); $err = []; if (!empty($languages)) { foreach ($languages as $lang => $videos) { foreach ($videos as $key => $video) { $wikiId = (int) WikiFactory::DomainToID($video['wiki_domain']); $video['wiki_id'] = $wikiId; $wiki = WikiFactory::getWikiByID($wikiId); $video['wiki_name'] = $wiki->city_title; $video['wiki_lang'] = $wiki->city_lang; $title = Title::newFromText($video['video_name'], NS_FILE); if (!empty($title) && $title->exists()) { $vid = wfFindFile($title); if (!empty($vid) && $vid instanceof WikiaLocalFile) { $handler = $vid->getHandler(); if ($handler instanceof OoyalaVideoHandler) { $metadata = $handler->getVideoMetadata(true); $video['video_id'] = $metadata['videoId']; $video['duration'] = WikiaFileHelper::formatDuration($metadata['duration']); $video['video_thumb'] = WikiaFileHelper::getMediaDetail($title)['imageUrl']; } else { $err[$video['video_name']] = self::VIDEO_IS_NOT_PROVIDED_BY_OOYALA; } } else { $err[$video['video_name']] = self::VIDEO_DOES_NOT_EXIST; } } else { $err[$video['video_name']] = self::VIDEO_DOES_NOT_EXIST; } $videos[$key] = $video; } $languages[$lang] = $videos; } } if (!empty($err)) { $this->response->setVal('error', $err); } else { $status = WikiFactory::setVarByName('wgWikiaGameGuidesSponsoredVideos', $this->wg->CityId, $languages); $this->response->setVal('status', $status); if ($status) { wfRunHooks('GameGuidesSponsoredVideosSave'); } } return true; }