1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36:
37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47:
48: class Media extends X2Model {
49:
50: public $supportsWorkflow = false;
51: public $_path;
52: public $_url;
53: protected $fieldFormatterClass = 'MediaFieldFormatter';
54:
55: 56: 57: 58:
59: public static function model($className = __CLASS__) {
60: return parent::model($className);
61: }
62:
63:
64:
65: private static ;
66: public static function () {
67: if (!isset (self::$_menuLogo)) {
68: $logo = Media::model()->findByAttributes(array(
69: 'associationId' => 1,
70: 'associationType' => 'logo'
71: ));
72: self::$_menuLogo = $logo;
73: }
74: return self::$_menuLogo;
75: }
76:
77: 78: 79:
80: public function tableName() {
81: return 'x2_media';
82: }
83:
84: 85: 86: 87:
88: private function collectGarbage (array $exclude = array ()) {
89: if (!in_array ($this->getPath (), $exclude) && file_exists($this->getPath())) {
90: $media = Media::model()->findByAttributes(array(
91: 'uploadedBy' => $this->uploadedBy,
92: 'fileName' => $this->fileName
93: ));
94:
95:
96:
97: if (!$media) {
98: unlink($this->getPath());
99: }
100: }
101: }
102:
103: public function afterDelete() {
104: parent::afterDelete();
105:
106: $this->_path = null;
107: $this->collectGarbage ();
108:
109:
110: if ($this->id === Yii::app()->settings->defaultTheme) {
111: Yii::app()->settings->defaultTheme = null;
112: Yii::app()->settings->enforceDefaultTheme = false;
113: Yii::app()->settings->save();
114: }
115: }
116:
117: public function resolveNameConflicts () {
118: $found = (int) Media::model()->countByAttributes (array('fileName' => $this->fileName));
119:
120:
121: if ($found) {
122: $count = 1;
123: $newName = $this->fileName;
124: $ext = CFileHelper::getExtension ($newName);
125: $base = preg_replace ('/\.'.preg_quote ($ext).'$/', '', $newName);
126: while ($found) {
127: $newName = "$base($count).$ext";
128: $found = (int) Media::model()->countByAttributes(array('fileName' => $newName));
129: $count++;
130: }
131: $this->fileName = $newName;
132: }
133: }
134:
135: public function beforeSave() {
136: if (empty($this->associationType)) {
137: $this->associationType = 'none';
138: }
139:
140: if (empty($this->uploadedBy)) {
141: $this->uploadedBy = Yii::app()->user->name;
142: }
143:
144: if (empty($this->name)) {
145: $this->name = $this->fileName;
146: }
147: if ($this->isNewRecord && $this->associationType !== 'theme' && empty($this->accessKey)) {
148: $this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
149: }
150:
151: $this->getPath();
152:
153: return parent::beforeSave();
154: }
155:
156: public function behaviors() {
157: $behaviors = array_merge(parent::behaviors(), array(
158: 'X2LinkableBehavior' => array(
159: 'class' => 'X2LinkableBehavior',
160: 'module' => 'media',
161: 'autoCompleteSource' => null
162: ),
163: 'ERememberFiltersBehavior' => array(
164: 'class' => 'application.components.ERememberFiltersBehavior',
165: 'defaults' => array(),
166: 'defaultStickOnClear' => false
167: )
168: ));
169: unset($behaviors['changelog']);
170: return $behaviors;
171: }
172:
173: 174: 175:
176: public function rules() {
177:
178:
179:
180: return array_merge (
181: $this->getBehaviorRules (),
182: array(
183: array('fileName', 'unique', 'on' => 'themeCreate'),
184: array('fileName', 'length', 'max' => 100),
185: )
186: );
187: }
188:
189: 190: 191:
192: public function relations() {
193:
194:
195: return array(
196: 'campainAttachments' => array(self::HAS_MANY, 'CampaignAttachment', 'media'),
197: );
198: }
199:
200: 201: 202:
203: 204: 205: 206: 207: 208: 209: 210: 211: 212:
213:
214: 215: 216: 217:
218: public function search() {
219:
220:
221:
222: $criteria = new CDbCriteria;
223: $username = Yii::app()->user->name;
224: if (!Yii::app()->params->isAdmin)
225: $criteria->addCondition("t.uploadedBy='$username' OR t.private=0 OR t.private=null");
226: $criteria->addCondition("t.associationType != 'theme'");
227: return $this->searchBase($criteria);
228: }
229:
230: public function isImage() {
231: return strpos($this->resolveType(), 'image/') === 0;
232: }
233:
234: 235: 236: 237: 238: 239: 240:
241: public static function isImageExt($filename) {
242: return (bool) preg_match('/\.(jpg|gif|png|bmp|jpeg|jpe)$/i', $filename);
243: }
244:
245:
246:
247: public function getImage($link = false, array $htmlOptions=array ()) {
248: if (!$this->fileExists() || !$this->isImage()) {
249: return '';
250: }
251:
252: if ($this->drive) {
253: return $this->googlePreview;
254: }
255:
256: $img = CHtml::image(
257: $this->getPublicUrl(), '', X2Html::mergeHtmlOptions (array(
258: 'class' => 'attachment-img',
259: ), $htmlOptions)
260: );
261:
262: if (!$link) {
263: return $img;
264: }
265:
266: return X2Html::link($img, $this->getPublicUrl());
267: }
268:
269: public function getGooglePreview() {
270: return X2Html::tag('iframe', array(
271: 'src' => $this->url . '/preview',
272: 'class' => 'google-drive'
273: ), '');
274: }
275:
276:
277: public function renderAttribute(
278: $fieldName, $makeLinks = true, $textOnly = true, $encode = true) {
279:
280: switch ($fieldName) {
281: case 'image':
282: $imageLink = $this->getImage (true);
283: return
284: '<div class="media-image">
285: <div class="full-size-screen">'.
286: X2Html::fa('expand').Yii::t('media', 'View Full Size').'
287: </div>
288: '. $imageLink .'
289: </div>';
290: break;
291: }
292:
293: return call_user_func_array ('parent::'.__FUNCTION__, func_get_args ());
294: }
295:
296: 297: 298: 299: 300: 301:
302: public function getPath() {
303: if (!isset($this->_path)) {
304: if ($this->associationType === 'logo') {
305: $this->_path = $this->fileName;
306: } else {
307: $pathFmt = array(
308: implode(DIRECTORY_SEPARATOR, array('{bp}', 'uploads', 'protected', 'media', '{uploadedBy}', '{fileName}')),
309: implode(DIRECTORY_SEPARATOR, array('{bp}', 'uploads', 'protected', '{fileName}')),
310: );
311: $basePath = realpath(Yii::app()->basePath . DIRECTORY_SEPARATOR . '..');
312: $params = array(
313: '{bp}' => $basePath,
314: '{uploadedBy}' => $this->uploadedBy,
315: '{fileName}' => $this->fileName
316: );
317: foreach ($pathFmt as $pfmt) {
318: $path = realpath(strtr($pfmt, $params));
319: if ((bool) $path) {
320: $this->_path = $path;
321: break;
322: } else {
323:
324: $this->_path = null;
325: }
326: }
327: }
328: }
329:
330: return $this->_path;
331: }
332:
333: 334: 335: 336: 337: 338: 339: 340:
341: public function resolveSize() {
342:
343: if (empty($this->filesize)) {
344: if (file_exists($this->path)) {
345: $this->filesize = filesize($this->path);
346: if (!$this->isNewRecord) {
347: $this->saveAttributes(array('filesize'));
348: }
349: } else {
350: $this->filesize = null;
351: }
352: }
353: return $this->filesize;
354: }
355:
356: 357: 358: 359: 360:
361: public function resolveDimensions() {
362: if (!$this->drive && $this->isImage()) {
363: if (empty($this->dimensions) && extension_loaded('gd') && !empty($this->path)) {
364: $sizeArr = getimagesize($this->path);
365: $this->dimensions = CJSON::encode(array(
366: 'width' => $sizeArr[0],
367: 'height' => $sizeArr[1],
368: ));
369: if (!$this->isNewRecord)
370: $this->saveAttributes(array('dimensions'));
371: }
372: }
373: return $this->dimensions;
374: }
375:
376: 377: 378: 379: 380:
381: public function getFmtSize() {
382: return FileUtil::formatSize($this->resolveSize());
383: }
384:
385: public function getFmtDimensions() {
386: if ($this->isImage()) {
387: $dim = CJSON::decode($this->resolveDimensions());
388: if (isset($dim['width'], $dim['height']))
389: return "{$dim['width']} x {$dim['height']}";
390: else
391: return null;
392: } else
393: return null;
394: }
395:
396: 397: 398: 399: 400: 401: 402: 403:
404: public function resolveType() {
405: if (empty($this->mimetype)) {
406: if (file_exists($this->path)) {
407: if ($finfo = FileUtil::finfo())
408: $this->mimetype = finfo_file($finfo, $this->path, FILEINFO_MIME);
409: else
410: $this->mimetype = null;
411: if (!$this->isNewRecord)
412: $this->saveAttributes(array('mimetype'));
413: } else {
414: $this->mimetype = null;
415: }
416: }
417: return $this->mimetype;
418: }
419:
420: public static function getFilePath($uploadedBy, $fileName) {
421: $possiblePaths = array(
422: "uploads/protected/media/{$uploadedBy}/{$fileName}",
423: "uploads/protected/{$fileName}",
424: );
425: foreach($possiblePaths as $path){
426: if (file_exists(implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, "..", $path)))) {
427: return $path;
428: }
429: }
430:
431: return null;
432: }
433:
434: 435: 436: 437:
438: public function getUrl() {
439: if (!isset($this->_url)) {
440: $relPath = self::getFilePath($this->uploadedBy, $this->fileName);
441:
442: if ($this->drive) {
443: $this->_url = "https://drive.google.com/file/d/" . $this->fileName;
444: } else if (file_exists(implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, "..", $relPath)))) {
445: $this->_url = Yii::app()->request->baseUrl . "/$relPath";
446: } else {
447: $this->_url = null;
448: }
449: }
450: return $this->_url;
451: }
452:
453: public static function getFileUrl($path) {
454: if ($path)
455: return Yii::app()->request->baseUrl . "/$path";
456: return null;
457: }
458:
459:
460:
461: public function getFullUrl() {
462: if ($path = self::getFilePath($this->uploadedBy, $this->fileName))
463: return Yii::app()->getBaseUrl(true) . "/$path";
464:
465: return null;
466: }
467:
468: public static function getFullFileUrl($path) {
469: if ($path)
470: return Yii::app()->getBaseUrl(true) . "/$path";
471:
472: return null;
473: }
474:
475:
476: public function getMediaLink() {
477: if ($this->drive) {
478: $name = $this->name;
479:
480: } else {
481: $name = $this->fileName;
482: }
483: return CHtml::link(
484: $this->fileName, Yii::app()->controller->createUrl('/media/', array('view' => $this->id)));
485: }
486:
487:
488: public function fileExists() {
489: if (file_exists(implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, "..", "uploads", "protected", "media", $this->uploadedBy, $this->fileName))))
490: return true;
491: else if (file_exists(implode(DIRECTORY_SEPARATOR, array(Yii::app()->basePath, "..", "uploads", "protected", $this->fileName))))
492: return true;
493: else if ($this->drive)
494: return true;
495:
496: return false;
497: }
498:
499:
500: private static function toBytes($size) {
501: if (!ctype_alpha(substr($size, -1))) {
502:
503: return $size;
504: }
505: if (strtolower(substr($size, -1)) === 'b') {
506:
507: $size = substr($size, 0, -1);
508: }
509: $type = strtolower(substr($size, -1));
510: $num = substr($size, 0, -1);
511: switch ($type) {
512: case 'p':
513: $num *= 1024;
514: case 't':
515: $num *= 1024;
516: case 'g':
517: $num *= 1024;
518: case 'm':
519: $num *= 1024;
520: case 'k':
521: $num *= 1024;
522: break;
523: }
524:
525: return $num;
526: }
527:
528:
529: public static function getServerMaxUploadSize() {
530: $max_post = Media::toBytes(ini_get('post_max_size'));
531: $max_upload_file = Media::toBytes(ini_get('upload_max_filesize'));
532: $max_upload_size = min($max_post, $max_upload_file);
533: $max_upload_size /= (1024 * 1024);
534: $max_upload_size = round($max_upload_size, 2);
535:
536: return $max_upload_size;
537: }
538:
539: public static function forbiddenFileTypes() {
540: return "exe, bat, dmg, js, jar, swf, php, pl, cgi, htaccess, py, rb";
541: }
542:
543: private static function getImageText($str, $makeLink, $makeImage, $media) {
544: $fileExists = $media->fileExists();
545:
546: if ($fileExists == false)
547: return $str . ' ' . Yii::t('media', '(deleted)');
548:
549: if ($makeLink && !Yii::app()->params->isMobileApp)
550: $str .= $media->getMediaLink();
551: else
552: $str .= "";
553:
554: if ($makeImage && $media->isImage()) {
555: $str .= '<br>'.$media->getImage();
556: }
557:
558: return $str;
559: }
560:
561: 562: 563: 564: 565: 566:
567: public static function attachmentSocialText($str, $makeLink = false, $makeImage = false) {
568:
569:
570: $matches = array();
571:
572: if (preg_match('/^<a href=".+media\/view\/([0-9]+)">.+<\/a>$/i', $str, $matches)) {
573: if (count($matches) == 2 && is_numeric($matches[1])) {
574:
575: $media = X2Model::model('Media')->findByPk($matches[1]);
576: if (isset($media)) {
577: $str = Yii::t('media', 'File:') . ' ';
578:
579: return self::getImageText($str, $makeLink, $makeImage, $media);
580: }
581: }
582: } elseif (preg_match('/^<a target="_blank" href="https:\/\/drive.google.com\/file\/d\/(.+)">.+<\/a>$/i', $str, $matches)) {
583: if (count($matches) == 2) {
584: $media = X2Model::model('Media')->findByAttributes(array('fileName' => $matches[1]));
585: if (isset($media)) {
586: $str = Yii::t('media', 'Google Drive:') . ' ';
587:
588: return self::getImageText($str, $makeLink, $makeImage, $media);
589: }
590: }
591: }
592: return x2base::convertUrls($str);
593: }
594:
595: public function getDownloadLink($text = null, $htmlOptions = array()) {
596: $text = !isset($text) ? '[' . CHtml::encode(Yii::t('media', 'Download')) . ']' : $text;
597:
598: $url = Yii::app()->createUrl ('/media/media/download', array ('id' => $this->id));
599:
600: return CHtml::link ($text, $url, $htmlOptions);
601: }
602:
603: public function renderFile() {
604: $filePath = $this->getPath();
605: if ($filePath != null) {
606: $file = Yii::app()->file->set($filePath);
607: } else {
608: throw new CHttpException(404);
609: }
610: if ($file->exists) {
611: header('Content-type: ' . $file->mimeType);
612: echo $file->getContents();
613: }
614: }
615:
616: public function getAccessKey() {
617: if(empty($this->accessKey)){
618: $this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
619: Yii::app()->db->createCommand()->update(
620: 'x2_media',
621: array('accessKey' => $this->accessKey),
622: 'id=:id',
623: array(':id' => $this->id));
624: }
625: return $this->accessKey;
626: }
627:
628: public function getPublicUrl($key = true) {
629: return Yii::app()->createExternalUrl('/media/media/getFile', array(
630: 'id' => $this->id,
631: 'key' => $key?$this->getAccessKey():'',
632: ));
633: }
634:
635: 636: 637: 638: 639: 640: 641: 642: 643:
644: public static function attachmentActionText($actionDescription, $makeLink = false, $makeImage = false) {
645:
646: $data = explode(':', $actionDescription);
647: $media = null;
648: if (count($data) == 2 && is_numeric($data[1]))
649: $media = X2Model::model('Media')->findByPK($data[1]);
650:
651: if ($media) {
652: if ($media->drive) {
653: $str = Yii::t('media', 'Google Drive:') . ' ';
654: } else {
655: $str = Yii::t('media', 'File:') . ' ';
656: }
657:
658: $fileExists = $media->fileExists();
659:
660: if ($fileExists == false)
661: return $str . $data[0] . ' ' . Yii::t('media', '(deleted)');
662:
663: if ($makeLink) {
664: $str .= $media->getMediaLink();
665: if (!$media->drive) {
666: $str .= " | " . CHtml::link('[Download]', array('/media/media/download', 'id' => $media->id));
667: }
668: } else
669: $str .= $data[0];
670:
671: if ($makeImage && $media->isImage())
672: $str .= $media->getImage();
673:
674: return $str;
675: } else
676: return $actionDescription;
677: }
678:
679: }
680: