Post.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. <?php
  2. /**
  3. * Copyright © 2015-2017 Ihor Vansach (ihor@magefan.com). All rights reserved.
  4. * See LICENSE.txt for license details (http://opensource.org/licenses/osl-3.0.php).
  5. *
  6. * Glory to Ukraine! Glory to the heroes!
  7. */
  8. namespace Magefan\Blog\Model;
  9. use Magefan\Blog\Model\Url;
  10. /**
  11. * Post model
  12. *
  13. * @method \Magefan\Blog\Model\ResourceModel\Post _getResource()
  14. * @method \Magefan\Blog\Model\ResourceModel\Post getResource()
  15. * @method int getStoreId()
  16. * @method $this setStoreId(int $value)
  17. * @method string getTitle()
  18. * @method $this setTitle(string $value)
  19. * @method string getMetaKeywords()
  20. * @method $this setMetaKeywords(string $value)
  21. * @method string getMetaDescription()
  22. * @method $this setMetaDescription(string $value)
  23. * @method string getIdentifier()
  24. * @method $this setIdentifier(string $value)
  25. * @method string getContent()
  26. * @method string getShortContent()
  27. * @method $this setContent(string $value)
  28. * @method string getContentHeading()
  29. * @method $this setContentHeading(string $value)
  30. */
  31. class Post extends \Magento\Framework\Model\AbstractModel
  32. {
  33. /**
  34. * Posts's Statuses
  35. */
  36. const STATUS_ENABLED = 1;
  37. const STATUS_DISABLED = 0;
  38. /**
  39. * Gallery images separator
  40. */
  41. const GALLERY_IMAGES_SEPARATOR = ';';
  42. /**
  43. * Base media folder path
  44. */
  45. const BASE_MEDIA_PATH = 'magefan_blog';
  46. /**
  47. * Prefix of model events names
  48. *
  49. * @var string
  50. */
  51. protected $_eventPrefix = 'magefan_blog_post';
  52. /**
  53. * Parameter name in event
  54. *
  55. * In observe method you can use $observer->getEvent()->getObject() in this case
  56. *
  57. * @var string
  58. */
  59. protected $_eventObject = 'blog_post';
  60. /**
  61. * @var \Magento\Framework\Math\Random
  62. */
  63. protected $random;
  64. /**
  65. * @var \Magento\Cms\Model\Template\FilterProvider
  66. */
  67. protected $filterProvider;
  68. /**
  69. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  70. */
  71. protected $scopeConfig;
  72. /**
  73. * @var \Magefan\Blog\Model\Url
  74. */
  75. protected $_url;
  76. /**
  77. * @var \Magefan\Blog\Model\AuthorFactory
  78. */
  79. protected $_authorFactory;
  80. /**
  81. * @var \Magefan\Blog\Model\ResourceModel\Category\CollectionFactory
  82. */
  83. protected $_categoryCollectionFactory;
  84. /**
  85. * @var \Magefan\Blog\Model\ResourceModel\Tag\CollectionFactory
  86. */
  87. protected $_tagCollectionFactory;
  88. /**
  89. * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
  90. */
  91. protected $_productCollectionFactory;
  92. /**
  93. * @var \Magefan\Blog\Model\ResourceModel\Category\Collection
  94. */
  95. protected $_parentCategories;
  96. /**
  97. * @var \Magefan\Blog\Model\ResourceModel\Tag\Collection
  98. */
  99. protected $_relatedTags;
  100. /**
  101. * @var \Magefan\Blog\Model\ResourceModel\Post\Collection
  102. */
  103. protected $_relatedPostsCollection;
  104. /**
  105. * @var \Magefan\Blog\Model\ImageFactory
  106. */
  107. protected $imageFactory;
  108. /**
  109. * @var string
  110. */
  111. protected $controllerName;
  112. /**
  113. * Initialize dependencies.
  114. *
  115. * @param \Magento\Framework\Model\Context $context
  116. * @param \Magento\Framework\Registry $registry
  117. * @param \Magento\Framework\Math\Random $random
  118. * @param \Magento\Cms\Model\Template\FilterProvider $filterProvider
  119. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  120. * @param \Magefan\Blog\Model\Url $url
  121. * @param \Magefan\Blog\Model\AuthorFactory $authorFactory
  122. * @param \Magefan\Blog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory
  123. * @param \Magefan\Blog\Model\ResourceModel\Tag\CollectionFactory $tagCollectionFactory
  124. * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
  125. * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
  126. * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
  127. * @param array $data
  128. */
  129. public function __construct(
  130. \Magento\Framework\Model\Context $context,
  131. \Magento\Framework\Registry $registry,
  132. \Magento\Framework\Math\Random $random,
  133. \Magento\Cms\Model\Template\FilterProvider $filterProvider,
  134. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  135. Url $url,
  136. \Magefan\Blog\Model\ImageFactory $imageFactory,
  137. \Magefan\Blog\Model\AuthorFactory $authorFactory,
  138. \Magefan\Blog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory,
  139. \Magefan\Blog\Model\ResourceModel\Tag\CollectionFactory $tagCollectionFactory,
  140. \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
  141. \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
  142. \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
  143. array $data = []
  144. ) {
  145. parent::__construct($context, $registry, $resource, $resourceCollection, $data);
  146. $this->filterProvider = $filterProvider;
  147. $this->random = $random;
  148. $this->scopeConfig = $scopeConfig;
  149. $this->_url = $url;
  150. $this->imageFactory = $imageFactory;
  151. $this->_authorFactory = $authorFactory;
  152. $this->_categoryCollectionFactory = $categoryCollectionFactory;
  153. $this->_tagCollectionFactory = $tagCollectionFactory;
  154. $this->_productCollectionFactory = $productCollectionFactory;
  155. $this->_relatedPostsCollection = clone($this->getCollection());
  156. }
  157. /**
  158. * Initialize resource model
  159. *
  160. * @return void
  161. */
  162. protected function _construct()
  163. {
  164. $this->_init('Magefan\Blog\Model\ResourceModel\Post');
  165. $this->controllerName = URL::CONTROLLER_POST;
  166. }
  167. /**
  168. * Retrieve controller name
  169. * @return string
  170. */
  171. public function getControllerName()
  172. {
  173. return $this->controllerName;
  174. }
  175. /**
  176. * Retrieve model title
  177. * @param boolean $plural
  178. * @return string
  179. */
  180. public function getOwnTitle($plural = false)
  181. {
  182. return $plural ? 'Posts' : 'Post';
  183. }
  184. /**
  185. * Retrieve true if post is active
  186. * @return boolean [description]
  187. */
  188. public function isActive()
  189. {
  190. return ($this->getStatus() == self::STATUS_ENABLED);
  191. }
  192. /**
  193. * Retrieve available post statuses
  194. * @return array
  195. */
  196. public function getAvailableStatuses()
  197. {
  198. return [self::STATUS_DISABLED => __('Disabled'), self::STATUS_ENABLED => __('Enabled')];
  199. }
  200. /**
  201. * Check if post identifier exist for specific store
  202. * return post id if post exists
  203. *
  204. * @param string $identifier
  205. * @param int $storeId
  206. * @return int
  207. */
  208. public function checkIdentifier($identifier, $storeId)
  209. {
  210. return $this->_getResource()->checkIdentifier($identifier, $storeId);
  211. }
  212. /**
  213. * Retrieve post url path
  214. * @return string
  215. */
  216. public function getUrl()
  217. {
  218. return $this->_url->getUrlPath($this->getIdentifier(), $this->controllerName);
  219. }
  220. /**
  221. * Retrieve post url
  222. * @return string
  223. */
  224. public function getPostUrl()
  225. {
  226. if (!$this->hasData('post_url')) {
  227. $url = $this->_url->getUrl($this, $this->controllerName);
  228. $this->setData('post_url', $url);
  229. }
  230. return $this->getData('post_url');
  231. }
  232. /**
  233. * Retrieve post canonical url
  234. * @return string
  235. */
  236. public function getCanonicalUrl()
  237. {
  238. return $this->_url->getCanonicalUrl($this);
  239. }
  240. /**
  241. * Retrieve featured image url
  242. * @return string
  243. */
  244. public function getFeaturedImage()
  245. {
  246. if (!$this->hasData('featured_image')) {
  247. if ($file = $this->getData('featured_img')) {
  248. $image = $this->_url->getMediaUrl($file);
  249. } else {
  250. $image = false;
  251. }
  252. $this->setData('featured_image', $image);
  253. }
  254. return $this->getData('featured_image');
  255. }
  256. /**
  257. * Set media gallery images url
  258. *
  259. * @param array $images
  260. * @return this
  261. */
  262. public function setGalleryImages(array $images)
  263. {
  264. $this->setData('media_gallery',
  265. implode(
  266. self::GALLERY_IMAGES_SEPARATOR,
  267. $images
  268. )
  269. );
  270. /* Reinit Media Gallery Images */
  271. $this->unsetData('gallery_images');
  272. $this->getGalleryImages();
  273. return $this;
  274. }
  275. /**
  276. * Retrieve media gallery images url
  277. * @return string
  278. */
  279. public function getGalleryImages()
  280. {
  281. if (!$this->hasData('gallery_images')) {
  282. $images = array();
  283. $gallery = explode(
  284. self::GALLERY_IMAGES_SEPARATOR,
  285. $this->getData('media_gallery')
  286. );
  287. if (!empty($gallery)) {
  288. foreach ($gallery as $file) {
  289. if ($file) {
  290. $images[] = $this->imageFactory->create()
  291. ->setFile($file);
  292. }
  293. }
  294. }
  295. $this->setData('gallery_images', $images);
  296. }
  297. return $this->getData('gallery_images');
  298. }
  299. /**
  300. * Retrieve first image url
  301. * @return string
  302. */
  303. public function getFirstImage()
  304. {
  305. if (!$this->hasData('first_image')) {
  306. $image = $this->getFeaturedImage();
  307. if (!$image) {
  308. $content = $this->getFilteredContent();
  309. $match = null;
  310. preg_match('/<img.+src=[\'"](?P<src>.+?)[\'"].*>/i', $content, $match);
  311. if (!empty($match['src'])) {
  312. $image = $match['src'];
  313. }
  314. }
  315. $this->setData('first_image', $image);
  316. }
  317. return $this->getData('first_image');
  318. }
  319. /**
  320. * Retrieve filtered content
  321. *
  322. * @return string
  323. */
  324. public function getFilteredContent()
  325. {
  326. $key = 'filtered_content';
  327. if (!$this->hasData($key)) {
  328. $content = $this->filterProvider->getPageFilter()->filter(
  329. $this->getContent()
  330. );
  331. $this->setData($key, $content);
  332. }
  333. return $this->getData($key);
  334. }
  335. /**
  336. * Retrieve short filtered content
  337. *
  338. * @return string
  339. */
  340. public function getShortFilteredContent()
  341. {
  342. $key = 'short_filtered_content';
  343. if (!$this->hasData($key)) {
  344. if ($this->getShortContent()) {
  345. $content = $this->filterProvider->getPageFilter()->filter(
  346. $this->getShortContent()
  347. );
  348. } else {
  349. $content = $this->getFilteredContent();
  350. $pageBraker = '<!-- pagebreak -->';
  351. $p = mb_strpos($content, $pageBraker);
  352. if (!$p) {
  353. $p = (int)$this->scopeConfig->getValue(
  354. 'mfblog/post_list/shortcotent_length',
  355. \Magento\Store\Model\ScopeInterface::SCOPE_STORE
  356. );
  357. }
  358. if ($p) {
  359. $content = mb_substr($content, 0, $p);
  360. try {
  361. libxml_use_internal_errors(true);
  362. $dom = new \DOMDocument();
  363. $dom->loadHTML('<?xml encoding="UTF-8">' . $content);
  364. $body = $dom->getElementsByTagName('body');
  365. if ($body && $body->length > 0) {
  366. $body = $body->item(0);
  367. $_content = new \DOMDocument;
  368. foreach ($body->childNodes as $child) {
  369. $_content->appendChild($_content->importNode($child, true));
  370. }
  371. $content = $_content->saveHTML();
  372. }
  373. } catch (\Exception $e) {
  374. }
  375. }
  376. }
  377. $this->setData($key, $content);
  378. }
  379. return $this->getData($key);;
  380. }
  381. /**
  382. * Retrieve meta title
  383. * @return string
  384. */
  385. public function getMetaTitle()
  386. {
  387. $title = $this->getData('meta_title');
  388. if (!$title) {
  389. $title = $this->getData('title');
  390. }
  391. return trim($title);
  392. }
  393. /**
  394. * Retrieve meta description
  395. * @return string
  396. */
  397. public function getMetaDescription()
  398. {
  399. $desc = $this->getData('meta_description');
  400. if (!$desc) {
  401. $desc = $this->getData('content');
  402. }
  403. $desc = strip_tags($desc);
  404. if (mb_strlen($desc) > 160) {
  405. $desc = mb_substr($desc, 0, 160);
  406. }
  407. return trim($desc);
  408. }
  409. /**
  410. * Retrieve og title
  411. * @return string
  412. */
  413. public function getOgTitle()
  414. {
  415. $title = $this->getData('og_title');
  416. if (!$title) {
  417. $title = $this->getMetaTitle();
  418. }
  419. return trim($title);
  420. }
  421. /**
  422. * Retrieve og description
  423. * @return string
  424. */
  425. public function getOgDescription()
  426. {
  427. $desc = $this->getData('og_description');
  428. if (!$desc) {
  429. $desc = $this->getMetaDescription();
  430. } else {
  431. $desc = strip_tags($desc);
  432. if (mb_strlen($desc) > 160) {
  433. $desc = mb_substr($desc, 0, 160);
  434. }
  435. }
  436. return trim($desc);
  437. }
  438. /**
  439. * Retrieve og type
  440. * @return string
  441. */
  442. public function getOgType()
  443. {
  444. $type = $this->getData('og_type');
  445. if (!$type) {
  446. $type = 'article';
  447. }
  448. return trim($type);
  449. }
  450. /**
  451. * Retrieve og image url
  452. * @return string
  453. */
  454. public function getOgImage()
  455. {
  456. if (!$this->hasData('og_image')) {
  457. if ($file = $this->getData('og_img')) {
  458. $image = $this->_url->getMediaUrl($file);
  459. } else {
  460. $image = false;
  461. }
  462. $this->setData('og_image', $image);
  463. }
  464. return $this->getData('og_image');
  465. }
  466. /**
  467. * Retrieve post parent categories
  468. * @return \Magefan\Blog\Model\ResourceModel\Category\Collection
  469. */
  470. public function getParentCategories()
  471. {
  472. if (is_null($this->_parentCategories)) {
  473. $this->_parentCategories = $this->_categoryCollectionFactory->create()
  474. ->addFieldToFilter('category_id', ['in' => $this->getCategories()])
  475. ->addStoreFilter($this->getStoreId())
  476. ->addActiveFilter()
  477. ->setOrder('position');
  478. }
  479. return $this->_parentCategories;
  480. }
  481. /**
  482. * Retrieve parent category
  483. * @return \Magefan\Blog\Model\Category || false
  484. */
  485. public function getParentCategory()
  486. {
  487. $k = 'parent_category';
  488. if (null === $this->getData($k)) {
  489. $this->setData($k, false);
  490. foreach ($this->getParentCategories() as $category) {
  491. if ($category->isVisibleOnStore($this->getStoreId())) {
  492. $this->setData($k, $category);
  493. break;
  494. }
  495. }
  496. }
  497. return $this->getData($k);
  498. }
  499. /**
  500. * Retrieve post parent categories count
  501. * @return int
  502. */
  503. public function getCategoriesCount()
  504. {
  505. return count($this->getParentCategories());
  506. }
  507. /**
  508. * Retrieve post tags
  509. * @return \Magefan\Blog\Model\ResourceModel\Tag\Collection
  510. */
  511. public function getRelatedTags()
  512. {
  513. if (is_null($this->_relatedTags)) {
  514. $this->_relatedTags = $this->_tagCollectionFactory->create()
  515. ->addFieldToFilter('tag_id', ['in' => $this->getTags()])
  516. ->setOrder('title');
  517. }
  518. return $this->_relatedTags;
  519. }
  520. /**
  521. * Retrieve post tags count
  522. * @return int
  523. */
  524. public function getTagsCount()
  525. {
  526. return count($this->getRelatedTags());
  527. }
  528. /**
  529. * Retrieve post related posts
  530. * @return \Magefan\Blog\Model\ResourceModel\Post\Collection
  531. */
  532. public function getRelatedPosts()
  533. {
  534. if (!$this->hasData('related_posts')) {
  535. $collection = $this->_relatedPostsCollection
  536. ->addFieldToFilter('post_id', ['neq' => $this->getId()])
  537. ->addStoreFilter($this->getStoreId());
  538. $collection->getSelect()->joinLeft(
  539. ['rl' => $this->getResource()->getTable('magefan_blog_post_relatedpost')],
  540. 'main_table.post_id = rl.related_id',
  541. ['position']
  542. )->where(
  543. 'rl.post_id = ?',
  544. $this->getId()
  545. );
  546. $this->setData('related_posts', $collection);
  547. }
  548. return $this->getData('related_posts');
  549. }
  550. /**
  551. * Retrieve post related products
  552. * @return \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
  553. */
  554. public function getRelatedProducts()
  555. {
  556. if (!$this->hasData('related_products')) {
  557. $collection = $this->_productCollectionFactory->create();
  558. if ($this->getStoreId()) {
  559. $collection->addStoreFilter($this->getStoreId());
  560. }
  561. $collection->getSelect()->joinLeft(
  562. ['rl' => $this->getResource()->getTable('magefan_blog_post_relatedproduct')],
  563. 'e.entity_id = rl.related_id',
  564. ['position']
  565. )->where(
  566. 'rl.post_id = ?',
  567. $this->getId()
  568. );
  569. $this->setData('related_products', $collection);
  570. }
  571. return $this->getData('related_products');
  572. }
  573. /**
  574. * Retrieve post author
  575. * @return \Magefan\Blog\Model\Author | false
  576. */
  577. public function getAuthor()
  578. {
  579. if (!$this->hasData('author')) {
  580. $author = false;
  581. if ($authorId = $this->getData('author_id')) {
  582. $_author = $this->_authorFactory->create();
  583. $_author->load($authorId);
  584. if ($_author->getId()) {
  585. $author = $_author;
  586. }
  587. }
  588. $this->setData('author', $author);
  589. }
  590. return $this->getData('author');
  591. }
  592. /**
  593. * Retrieve if is visible on store
  594. * @return bool
  595. */
  596. public function isVisibleOnStore($storeId)
  597. {
  598. return $this->getIsActive()
  599. && $this->getData('publish_time') <= $this->getResource()->getDate()->gmtDate()
  600. && array_intersect([0, $storeId], $this->getStoreIds());
  601. }
  602. /**
  603. * Retrieve if is preview secret is valid
  604. * @return bool
  605. */
  606. public function isValidSecret($secret)
  607. {
  608. return ($secret && $this->getSecret() === $secret);
  609. }
  610. /**
  611. * Retrieve post publish date using format
  612. * @param string $format
  613. * @return string
  614. */
  615. public function getPublishDate($format = 'Y-m-d H:i:s')
  616. {
  617. return \Magefan\Blog\Helper\Data::getTranslatedDate(
  618. $format,
  619. $this->getData('publish_time')
  620. );
  621. }
  622. /**
  623. * Retrieve post publish date using format
  624. * @param string $format
  625. * @return string
  626. */
  627. public function getUpdateDate($format = 'Y-m-d H:i:s')
  628. {
  629. return \Magefan\Blog\Helper\Data::getTranslatedDate(
  630. $format,
  631. $this->getData('update_time')
  632. );
  633. }
  634. /**
  635. * Temporary method to get images from some custom blog version. Do not use this method.
  636. * @param string $format
  637. * @return string
  638. */
  639. public function getPostImage()
  640. {
  641. $image = $this->getData('featured_img');
  642. if (!$image) {
  643. $image = $this->getData('post_image');
  644. }
  645. return $image;
  646. }
  647. /**
  648. * Prepare all additional data
  649. * @param string $format
  650. * @return self
  651. */
  652. public function initDinamicData()
  653. {
  654. $keys = [
  655. 'og_image',
  656. 'og_type',
  657. 'og_description',
  658. 'og_title',
  659. 'meta_description',
  660. 'meta_title',
  661. 'short_filtered_content',
  662. 'filtered_content',
  663. 'first_image',
  664. 'featured_image',
  665. 'post_url',
  666. ];
  667. foreach ($keys as $key) {
  668. $method = 'get' . str_replace('_', '',
  669. ucwords($key, '_')
  670. );
  671. $this->$method();
  672. }
  673. return $this;
  674. }
  675. /**
  676. * Duplicate post and return new object
  677. * @return self
  678. */
  679. public function duplicate()
  680. {
  681. $object = clone $this;
  682. $object
  683. ->unsetData('post_id')
  684. ->setTitle($object->getTitle() . ' (' . __('Duplicated') . ')')
  685. ->setData('is_active', 0);
  686. $relatedProductIds = $this->getRelatedProducts()->getAllIds();
  687. $relatedPpostIds = $this->getRelatedPosts()->getAllIds();
  688. $object->setData(
  689. 'links',
  690. [
  691. 'product' => array_combine($relatedProductIds, $relatedProductIds),
  692. 'post' => array_combine($relatedPpostIds, $relatedPpostIds),
  693. ]
  694. );
  695. return $object->save();
  696. }
  697. /**
  698. * Retrieve secret key of post, it can be used during preview
  699. * @return string
  700. */
  701. public function getSecret()
  702. {
  703. if ($this->getId() && !$this->getData('secret')) {
  704. $this->setData(
  705. 'secret',
  706. $this->random->getRandomString(32)
  707. );
  708. $this->save();
  709. }
  710. return $this->getData('secret');
  711. }
  712. }