1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\Modules\OEmbedFiles;
9:
10: /**
11: * This module extends functionality of Files module.
12: * It provides ability to add shortcuts based on [oembed](http://oembed.com/) data format.
13: *
14: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
15: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
16: * @copyright Copyright (c) 2023, Afterlogic Corp.
17: *
18: * @package Modules
19: */
20: class Module extends \Aurora\System\Module\AbstractModule
21: {
22: protected $aProviders = array();
23:
24: /***** private functions *****/
25: /**
26: * Initializes module.
27: *
28: * @ignore
29: */
30: public function init()
31: {
32: $this->loadProviders();
33:
34: $this->subscribeEvent('Files::GetLinkType', array($this, 'onGetLinkType'));
35: $this->subscribeEvent('Files::CheckUrl', array($this, 'onCheckUrl'));
36: $this->subscribeEvent('Files::PopulateFileItem::after', array($this, 'onAfterPopulateFileItem'));
37: }
38:
39: /**
40: * Returns **true** if oembed file info for specified link was found.
41: *
42: * @ignore
43: * @param string $Link File link.
44: * @param boolean $Result Is passed by reference.
45: * @return boolean
46: */
47: public function onGetLinkType($Link, &$Result)
48: {
49: $Result = !!($this->getOembedFileInfo($Link));
50: return $Result; // break or not executing of event handlers
51: }
52:
53: /**
54: * Writes to $mResult variable information about link.
55: *
56: * @ignore
57: * @param string $sUrl
58: * @param array $mResult
59: */
60: public function onCheckUrl($aArgs, &$mResult)
61: {
62: $iUserId = \Aurora\System\Api::getAuthenticatedUserId();
63:
64: if ($iUserId) {
65: if (!empty($aArgs['Url'])) {
66: $oInfo = $this->getOembedFileInfo($aArgs['Url']);
67: if ($oInfo) {
68: $mResult['Size'] = isset($oInfo->fileSize) ? $oInfo->fileSize : '';
69: $mResult['Name'] = isset($oInfo->title) ? $oInfo->title : '';
70: $mResult['LinkType'] = 'oembeded';
71: $mResult['Thumb'] = isset($oInfo->thumbnail_url) ? $oInfo->thumbnail_url : null;
72: }
73: }
74: }
75: }
76:
77: /**
78: * Populates file item.
79: *
80: * @ignore
81: * @param \Aurora\Modules\Files\Classes\FileItem $oItem
82: * @return boolean
83: */
84: public function onAfterPopulateFileItem($aArgs, &$oItem)
85: {
86: $bBreak = false;
87: if ($oItem->IsLink) {
88: $Result = $this->getOembedFileInfo($oItem->LinkUrl);
89:
90: if ($Result) {
91: $oItem->LinkType = 'oembeded';
92: // $oItem->Name = isset($Result->title) ? $Result->title : $oItem->Name;
93: $oItem->Size = isset($Result->fileSize) ? $Result->fileSize : $oItem->Size;
94: $oItem->OembedHtml = isset($Result->html) ? $Result->html : $oItem->OembedHtml;
95: $oItem->Thumb = true;
96: $oItem->ThumbnailUrl = $Result->thumbnailUrl;
97: $oItem->IsExternal = true;
98: }
99: $bBreak = !!$Result;
100: }
101: return $bBreak; // break or not executing of event handlers
102: }
103:
104: /**
105: * Returns Oembed information for file.
106: *
107: * @param string $sUrl
108: * @return stdClass
109: */
110: protected function getOembedFileInfo($sUrl)
111: {
112: $mResult = false;
113: $sOembedUrl = '';
114:
115: foreach ($this->aProviders as $aProvider) {
116: if (\preg_match("/".$aProvider['patterns']."/", $sUrl)) {
117: $sOembedUrl = $aProvider['url'].$sUrl;
118: break;
119: }
120: }
121:
122: if (false !== \strpos($sUrl, 'instagram.com')) {
123: $sUrl = \str_replace('instagram.com', 'instagr.am', $sUrl);
124: $sOembedUrl = 'https://api.instagram.com/oembed?url='.$sUrl;
125: }
126:
127: if (\strlen($sOembedUrl) > 0) {
128: $oCurl = \curl_init();
129: \curl_setopt_array($oCurl, array(
130: CURLOPT_URL => $sOembedUrl,
131: CURLOPT_HEADER => 0,
132: CURLOPT_RETURNTRANSFER => true,
133: CURLOPT_FOLLOWLOCATION => true,
134: CURLOPT_ENCODING => '',
135: CURLOPT_AUTOREFERER => true,
136: CURLOPT_SSL_VERIFYPEER => false, //required for https urls
137: CURLOPT_CONNECTTIMEOUT => 5,
138: CURLOPT_TIMEOUT => 5,
139: CURLOPT_MAXREDIRS => 5
140: ));
141: $sResult = \curl_exec($oCurl);
142: \curl_close($oCurl);
143: $oResult = \json_decode($sResult);
144:
145: if ($oResult) {
146: $sSearch = $oResult->html;
147: $aPatterns = array('/ width="\d+."/', '/ height="\d+."/', '/(src="[^\"]+)/');
148: $aResults = array(' width="896"', ' height="504"', '$1?&autoplay=1&auto_play=true');
149: $oResult->html =\preg_replace($aPatterns, $aResults, $sSearch);
150:
151: $aRemoteFileInfo = \Aurora\System\Utils::GetRemoteFileInfo($sUrl);
152: $oResult->fileSize = $aRemoteFileInfo['size'];
153:
154: $oResult->thumbnailUrl = isset($oResult->thumbnail_url) ? $oResult->thumbnail_url : '';
155: $mResult = $oResult;
156: }
157: }
158:
159: return $mResult;
160: }
161:
162: /**
163: * Loads providers from file.
164: */
165: protected function loadProviders()
166: {
167: $sFile = __DIR__.DIRECTORY_SEPARATOR.'providers.json';
168: if (\file_exists($sFile)) {
169: $sJsonData = \file_get_contents($sFile);
170: $aJsonData = \json_decode($sJsonData, true);
171: foreach ($aJsonData as $aProvider) {
172: $this->aProviders[$aProvider['title']] = array(
173: 'patterns' => $aProvider['url_re'],
174: 'url' => $aProvider['endpoint_url']
175: );
176: }
177: }
178: }
179: /***** private functions *****/
180: }
181: