Rss.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. <?php
  2. namespace rss;
  3. use Yii;
  4. use rss\BaseModule;
  5. use yii\base\InvalidConfigException;
  6. use yii\helpers\ArrayHelper;
  7. use yii\helpers\Url;
  8. use \app\models\News;
  9. use yii\base\Module;
  10. use app\models\Authors;
  11. use yii\db\Expression;
  12. /**
  13. * Yandex.Turbo module definition class
  14. */
  15. class Rss extends BaseModule
  16. {
  17. /**
  18. * {@inheritdoc}
  19. */
  20. public $controllerNamespace = 'rss\controllers';
  21. /**
  22. * {@inheritdoc}
  23. */
  24. public $defaultRoute = "default";
  25. /**
  26. * @var string, the name of module
  27. */
  28. public $name = "Yandex.Turbo";
  29. /**
  30. * @var string, the description of module
  31. */
  32. public $description = "Новости Алтайского края";
  33. /**
  34. * @var array list of supported models for displaying a Turbo-pages feed
  35. */
  36. public $supportModels = [
  37. 'News' => '\models\News',
  38. ];
  39. /**
  40. * @var int cache lifetime, `0` - for not use cache
  41. */
  42. public $cacheExpire = 180;
  43. /**
  44. * @var array default channel options
  45. */
  46. public $channelOptions = [];
  47. /**
  48. * @var string default route to render Turbo-pages feed (use "/" - for root)
  49. */
  50. public $turboRoute = "/rss/turbo";
  51. /**
  52. * @var string the module version
  53. */
  54. private $version = "1.0.4";
  55. /**
  56. * @var integer, priority of initialization
  57. */
  58. private $priority = 5;
  59. /**
  60. * {@inheritdoc}
  61. */
  62. public function init()
  63. {
  64. parent::init();
  65. // Set version of current module
  66. $this->setVersion($this->version);
  67. // Set priority of current module
  68. $this->setPriority($this->priority);
  69. // Process and normalize route for frontend
  70. $this->turboRoute = self::normalizeRoute($this->turboRoute);
  71. // $this->bootstrap(Yii::$app);
  72. }
  73. /**
  74. * {@inheritdoc}
  75. */
  76. public function dashboardNavItems($createLink = false)
  77. {
  78. $items = [
  79. 'label' => $this->name,
  80. 'icon' => 'fa fa-fw fa-rocket',
  81. 'url' => [$this->routePrefix . '/'. $this->id],
  82. 'active' => (in_array(\Yii::$app->controller->module->id, [$this->id]) && Yii::$app->controller->id == 'list'),
  83. ];
  84. return $items;
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public function bootstrap($app)
  90. {
  91. parent::bootstrap($app);
  92. if (isset(Yii::$app->params["turbo.supportModels"]))
  93. $this->supportModels = Yii::$app->params["turbo.supportModels"];
  94. if (isset(Yii::$app->params["turbo.cacheExpire"]))
  95. $this->cacheExpire = Yii::$app->params["turbo.cacheExpire"];
  96. if (isset(Yii::$app->params["turbo.channelOptions"]))
  97. $this->channelOptions = Yii::$app->params["turbo.channelOptions"];
  98. if (isset(Yii::$app->params["turbo.turboRoute"]))
  99. $this->turboRoute = Yii::$app->params["turbo.turboRoute"];
  100. if (!isset($this->supportModels))
  101. throw new InvalidConfigException("Required module property `supportModels` isn't set.");
  102. if (!isset($this->cacheExpire))
  103. throw new InvalidConfigException("Required module property `cacheExpire` isn't set.");
  104. if (!isset($this->channelOptions))
  105. throw new InvalidConfigException("Required module property `channelOptions` isn't set.");
  106. if (!isset($this->turboRoute))
  107. throw new InvalidConfigException("Required module property `turboRoute` isn't set.");
  108. if (!is_array($this->supportModels))
  109. throw new InvalidConfigException("Module property `supportModels` must be array.");
  110. if (!is_array($this->channelOptions))
  111. throw new InvalidConfigException("Module property `channelOptions` must be array.");
  112. if (!is_integer($this->cacheExpire))
  113. throw new InvalidConfigException("Module property `cacheExpire` must be integer.");
  114. if (!is_string($this->turboRoute))
  115. throw new InvalidConfigException("Module property `turboRoute` must be a string.");
  116. // Add route to pass turbo-pages in frontend
  117. $turboRoute = $this->turboRoute;
  118. if (empty($turboRoute) || $turboRoute == "/") {
  119. $app->getUrlManager()->addRules([
  120. [
  121. 'pattern' => '/rss',
  122. 'route' => '/rss/default',
  123. 'suffix' => '.xml'
  124. ],
  125. '/turbo.xml' => '/rss/default'
  126. ], true);
  127. } else if (is_string($turboRoute)) {
  128. $app->getUrlManager()->addRules([
  129. [
  130. 'pattern' => $turboRoute . '/feed',
  131. 'route' => 'rss/default',
  132. 'suffix' => '.xml'
  133. ],
  134. $turboRoute . '/feed.xml' => 'rss/default'
  135. ], true);
  136. $app->getUrlManager()->addRules([
  137. [
  138. 'pattern' => $turboRoute . '/turbo',
  139. 'route' => 'rss/default/turbo',
  140. 'suffix' => '.xml'
  141. ],
  142. $turboRoute . '/feed.xml' => 'rss/default'
  143. ], true);
  144. $app->getUrlManager()->addRules([
  145. [
  146. 'pattern' => $turboRoute . '/dzen',
  147. 'route' => 'rss/default/dzen',
  148. 'suffix' => '.xml'
  149. ],
  150. $turboRoute . '/dzen.xml' => 'rss/default'
  151. ], true);
  152. $app->getUrlManager()->addRules([
  153. [
  154. 'pattern' => $turboRoute . '/ria',
  155. 'route' => 'rss/default/ria',
  156. 'suffix' => '.xml'
  157. ],
  158. $turboRoute . '/ria.xml' => 'rss/default'
  159. ], true);
  160. }
  161. // echo $turboRoute;
  162. // Attach to events of create/change/remove of models for the subsequent clearing cache of feeds
  163. if (!($app instanceof \yii\console\Application)) {
  164. if ($cache = $app->getCache()) {
  165. if (is_array($models = $this->supportModels)) {
  166. foreach ($models as $name => $class) {
  167. if (class_exists($class)) {
  168. $model = new $class();
  169. \yii\base\Event::on($class, $model::EVENT_AFTER_INSERT, function ($event) use ($cache) {
  170. $cache->delete(md5('yandex-turbo'));
  171. });
  172. \yii\base\Event::on($class, $model::EVENT_AFTER_UPDATE, function ($event) use ($cache) {
  173. $cache->delete(md5('yandex-dzen'));
  174. });
  175. \yii\base\Event::on($class, $model::EVENT_AFTER_DELETE, function ($event) use ($cache) {
  176. $cache->delete(md5('yandex-ria'));
  177. });
  178. }
  179. }
  180. }
  181. }
  182. }
  183. }
  184. /**
  185. * Generate current RSS-feed URL
  186. *
  187. * @return null|string
  188. */
  189. public function getFeedURL() {
  190. $url = null;
  191. $turboRoute = $this->turboRoute;
  192. if (empty($turboRoute) || $turboRoute == "/") {
  193. $url = Url::to('/turbo.xml', true);
  194. } else {
  195. $url = Url::to($turboRoute . '/feed.xml', true);
  196. }
  197. return $url;
  198. }
  199. /**
  200. * Get items for building a Yandex.Turbo pages
  201. *
  202. * @return array
  203. */
  204. public function getTurboItems() {
  205. $items = [];
  206. if (is_array($models = $this->supportModels)) {
  207. foreach ($models as $name => $class) {
  208. // If class of model exist
  209. if (class_exists($class)) {
  210. $model = new $class();
  211. $newsDataProvider = new \yii\data\ActiveDataProvider(
  212. [
  213. "query"=>\app\models\News::find()->andwhere(['active'=>'Y'])->andWhere(['between', 'news.dt_pub', date("Y-m-d H:i:00",time()-2678400*1 ) , date("Y-m-d H:i:00")])->orderBy(["dt_pub"=>SORT_DESC])->limit(200),
  214. "pagination" =>[
  215. "pageSize"=>200
  216. ],
  217. ]
  218. );
  219. // If module is loaded
  220. if (1) {
  221. $append = [];
  222. foreach ($newsDataProvider->getModels() as $item) {
  223. $turbo = true;
  224. if( stripos( $item->text, '<script') !== false ) $turbo = false;
  225. if( stripos( $item->text, '##_gallery-') !== false ) $turbo = false;
  226. //Сюжеты
  227. $story = new \app\models\base\Story();
  228. $stn = $story->getForNews($item->id);
  229. $tstory = null;
  230. if( count($stn) > 0 ){
  231. $tstory = $stn[0]->url?:null;
  232. }
  233. // Рубрики
  234. $topic = $item->getTopics()->andWhere(['show'=>"Y","active"=>1])->one();
  235. $tcategory = null;
  236. $stopnews = false;
  237. $riatop = false;
  238. if($topic instanceof \app\models\base\NewsTopic){
  239. $tcategory = $topic->title;
  240. }
  241. $atopics = [];
  242. $topics = $item->getTopics()->andWhere(['show'=>"Y","active"=>1])->all();
  243. foreach( $topics as $aa ){
  244. $atopics[] = ['title'=>$aa->title,'url'=>$aa->getUrl(true)];
  245. if( $aa->id == 109 ) $riatop = true;
  246. if( $aa->id == 137 ) $riatop = true;
  247. }
  248. if( $item->show_author == 'Y' )
  249. {
  250. $author = Authors::getAuthor( $item->author );
  251. $authorName = $author->name;
  252. }else{
  253. $authorName = 'ИА Амител';
  254. }
  255. // $stopnews = false;
  256. $append[] = [
  257. 'url' => $item->getUrl(),
  258. 'name' => (isset($item->name)) ? $item->name : null,
  259. 'title' => (isset($item->title)) ? $item->title : null,
  260. 'image' => ($item->getPreview()) ? $item->getPreview()->getUrl(2,'jpg',true) : null,
  261. 'image_title' => (isset($item->photo_title)) ? $item->photo_title : null,
  262. 'description' => (isset($item->lid)) ? trim($item->lid) : ((isset($item->description)) ? trim($item->description) : null),
  263. 'content' => (isset($item->text)) ? self::prepare($item->text, $item) : null,
  264. 'newscontent' => (isset($item->text)) ? self::prepare($item->text, $item) : null,
  265. 'stext' => (isset($item->text)) ? self::prepare2($item->text, $item) : null,
  266. 'updated_at' => (isset($item->dt_pub)) ? $item->dt_pub : null,
  267. 'status' => $turbo, // отключение турбо
  268. 'export_rss' => $item->export_rss,
  269. 'type' => $item->type,
  270. 'story' => null, //$tstory,
  271. 'category'=> $tcategory,
  272. 'topics'=>$atopics,
  273. 'uid' => $item->uid,
  274. 'author'=>$authorName,
  275. 'top'=>($item->top == 'Y' || $item->NH == 'Y' || $item->NH == 'F'),
  276. 'riatop'=>$riatop,
  277. 'stopnews' =>$stopnews
  278. ];
  279. };
  280. //print_r($append);
  281. $items = ArrayHelper::merge($items, $append);
  282. }
  283. }
  284. }
  285. }
  286. return $items;
  287. }
  288. /**
  289. * Get items for building a Yandex.Turbo pages
  290. *
  291. * @return array
  292. */
  293. public function getTopicsItems($gtopic) {
  294. $items = [];
  295. if (is_array($models = $this->supportModels)) {
  296. foreach ($models as $name => $class) {
  297. // If class of model exist
  298. if (class_exists($class)) {
  299. $model = new $class();
  300. $newsDataProvider = new \yii\data\ActiveDataProvider(
  301. [
  302. "query"=>\app\models\News::find()->from(new Expression("news FORCE INDEX (top,calendar) , news_topic_relation t USE index(news_id)"))
  303. ->andwhere(['active'=>'Y'])->andWhere(['between', 'news.dt_pub', "2023-06-27 12:00:00" , date("Y-m-d H:i:00")])->orderBy(["dt_pub"=>SORT_DESC])
  304. ->andWhere(new Expression('t.news_id=id'))
  305. ->andWhere(['t.topic_id'=>$gtopic])
  306. ->limit(200),
  307. "pagination" =>[
  308. "pageSize"=>200
  309. ],
  310. ]
  311. );
  312. // If module is loaded
  313. if (1) {
  314. $append = [];
  315. foreach ($newsDataProvider->getModels() as $item) {
  316. $turbo = true;
  317. if( stripos( $item->text, '<script') !== false ) $turbo = false;
  318. if( stripos( $item->text, '##_gallery-') !== false ) $turbo = false;
  319. //Сюжеты
  320. $story = new \app\models\base\Story();
  321. $stn = $story->getForNews($item->id);
  322. $tstory = null;
  323. if( count($stn) > 0 ){
  324. $tstory = $stn[0]->url?:null;
  325. }
  326. // Рубрики
  327. $topic = $item->getTopics()->andWhere(['show'=>"Y","active"=>1])->one();
  328. $tcategory = null;
  329. if($topic instanceof \app\models\base\NewsTopic){
  330. $tcategory = $topic->title;
  331. }
  332. $atopics = [];
  333. $topics = $item->getTopics()->andWhere(['show'=>"Y","active"=>1])->all();
  334. $stopnews = false;
  335. foreach( $topics as $aa ){
  336. $atopics[] = ['title'=>$aa->title,'url'=>$aa->getUrl(true)];
  337. }
  338. if( $item->show_author == 'Y' )
  339. {
  340. $author = Authors::getAuthor( $item->author );
  341. $authorName = $author->name;
  342. }else{
  343. $authorName = 'ИА Амител';
  344. }
  345. $re = '|<div[^>]+>(\d+)<\/div>|is';
  346. $text = preg_replace($re, '', $item->text );
  347. $append[] = [
  348. 'url' => $item->getUrl(),
  349. 'name' => (isset($item->name)) ? $item->name : null,
  350. 'title' => (isset($item->title)) ? $item->title : null,
  351. 'image' => ($item->getPreview()) ? $item->getPreview()->getUrl(2,'jpg',true) : null,
  352. 'description' => (isset($item->lid)) ? trim($item->lid) : ((isset($item->description)) ? trim($item->description) : null),
  353. 'content' => (isset($item->text)) ? self::prepare($text, $item) : null,
  354. 'newscontent' => (isset($item->text)) ? self::prepare($text, $item) : null,
  355. 'stext' => (isset($item->text)) ? self::prepare2($text, $item) : null,
  356. 'updated_at' => (isset($item->dt_pub)) ? $item->dt_pub : null,
  357. 'status' => $turbo, // отключение турбо
  358. 'export_rss' => $item->export_rss,
  359. 'type' => $item->type,
  360. 'story' => null, //$tstory,
  361. 'category'=> $tcategory,
  362. 'topics'=>$atopics,
  363. 'uid' => $item->uid,
  364. 'author'=>$authorName,
  365. 'top'=>($item->top == 'Y' || $item->NH == 'Y' || $item->NH == 'F'),
  366. 'stopnews' =>$stopnews
  367. ];
  368. };
  369. //print_r($append);
  370. $items = ArrayHelper::merge($items, $append);
  371. }
  372. }
  373. }
  374. }
  375. return $items;
  376. }
  377. public function prepare( $text, $item )
  378. {
  379. // $f = News::find()->andWhere(['id'=>$item->id])->one();
  380. $text = $this->processInjects($text);
  381. $text = $this->processGalleriesInjects($text);
  382. $text = preg_replace('|src="/([^/])|im','src="https://www.amic.ru/${1}',$text);
  383. $text = preg_replace('|href="/([^/])|im','href="https://www.amic.ru/${1}',$text);
  384. $text = preg_replace('/[\x00-\x09\x0B-\x0C\x0E-\x1F]/', '', $text);
  385. $text = preg_replace("/<span class=\"style3\">.+<\/span>/i",'',$text);
  386. $text = str_replace('&shy;','',$text);
  387. $text = str_replace('&nbsp;',' ',$text);
  388. $text = str_replace('<p> </p>',' ',$text);
  389. $text = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $text);
  390. //$text = News::processInjects($text);
  391. // var_dump($f);
  392. //$text = $f->renderBody();
  393. // $f->renderBody();
  394. // $text = CutLastInject( $text, $a['id'] );
  395. // $text = ShowInject( $text );
  396. // $text = str_replace( 'id="inject"', '', $text );
  397. // $text = ShowGalleryWidgetm( $text );
  398. $text = strip_tags( $text,'<p></p><br></br><span></span><div></div><img><a></a><i></i><b></b><sup></sup><ul></ul><li></li><table></table><td></td><tr></tr><h2></h2><h3></h3><blockquote></blockquote>' );
  399. $text = str_replace('<p></p>',' ',$text);
  400. $text = preg_replace('#\s(id)="[^"]+"#', '', $text);
  401. return $text;
  402. }
  403. public function prepare2( $text, $item )
  404. {
  405. // $f = News::find()->andWhere(['id'=>$item->id])->one();
  406. //$text = $this->processInjects($text);
  407. if( $item->embed_url != '' ){
  408. $text = '<iframe src="'.$item->embed_url.'" style="position: absolute; z-index: 2;" title="video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" width="100%" height="100%" frameborder="0"></iframe>'.$text;
  409. }
  410. $text = $this->processGalleriesInjects($text);
  411. $text = preg_replace('|src="/([^/])|im','src="https://www.amic.ru/${1}',$text);
  412. $text = preg_replace('|href="/([^/])|im','href="https://www.amic.ru/${1}',$text);
  413. $text = preg_replace('/[\x00-\x09\x0B-\x0C\x0E-\x1F]/', '', $text);
  414. $text = preg_replace("/<span class=\"style3\">.+<\/span>/i",'',$text);
  415. $text = str_replace('&shy;','',$text);
  416. $text = str_replace('&nbsp;',' ',$text);
  417. $text = str_replace('<p> </p>',' ',$text);
  418. $text = preg_replace('#<script(.*?)>(.*?)</script>#is', '', $text);
  419. //$text = News::processInjects($text);
  420. // var_dump($f);
  421. //$text = $f->renderBody();
  422. // $f->renderBody();
  423. // $text = CutLastInject( $text, $a['id'] );
  424. // $text = ShowInject( $text );
  425. // $text = str_replace( 'id="inject"', '', $text );
  426. // $text = ShowGalleryWidgetm( $text );
  427. $re = '|<div[^>]+>(\s+)<\/div>|is';
  428. $text = preg_replace($re, '', $text );
  429. $text = strip_tags( $text,'<p></p><br></br><span></span><div></div><img><a></a><i></i><b></b><sup></sup><ul></ul><li></li><table></table><td></td><tr></tr><h2></h2><h3></h3><blockquote></blockquote><iframe></iframe>' );
  430. $text = str_replace('<p></p>',' ',$text);
  431. $text = preg_replace('#\s(id)="[^"]+"#', '', $text);
  432. return $text;
  433. }
  434. public function processInjects($text)
  435. {
  436. $re = '/##news_(.*)##/mU';
  437. $res = preg_replace_callback($re,function (array $matches): string
  438. {
  439. $post = News::findOne(['uid'=>ArrayHelper::getValue($matches,1)]);
  440. if($post instanceof News) {
  441. return \Yii::$app->view->render("@app/views/news/view/inject",["post"=>$post]);
  442. } else {
  443. return "";
  444. }
  445. },$text);
  446. if( strlen($res) != strlen($text) ) return $res;
  447. $re = "|<div[^>]+>.[^<]*</div>|U";
  448. $res = preg_replace_callback($re,function (array $matches): string
  449. {
  450. $xml = simplexml_load_string( str_replace( '&nbsp;', '', ArrayHelper::getValue($matches,0) ), 'SimpleXMLElement', LIBXML_NOCDATA );
  451. if( $xml ){
  452. $attributes =$xml->attributes();
  453. if( $attributes['class'] == 'insinject' || $attributes['id'] == "inject" ){
  454. $r = preg_match( "/^https?:\/\/(.*)\/(.*)\/(\d+)\/$/i", $attributes['url'], $aa );
  455. $id = 0;
  456. if( $r && ($aa[1] == 'www.amic.ru' || $aa[1] == Yii::$app->request->serverName) && $aa[3]*1 > 0 ){
  457. // old style inject
  458. $id = $aa[3]*1;
  459. }
  460. $r = preg_match( "/^https?:\/\/(.*)\/.*(\d+)\/?$/iU", $attributes['url'], $aa );
  461. if( $r && ( $aa[1] == Yii::$app->request->serverName || $aa[1] == 'www.amic.ru' ) && $aa[2]*1 > 0 ){
  462. // new style inject
  463. $id = $aa[2]*1;
  464. }
  465. if( $id ){
  466. //$attributes['type'] to do
  467. $post = News::findOne(['id'=>$id]);
  468. if($post instanceof News) {
  469. if( $attributes['type'] == 1 ){
  470. return \Yii::$app->view->render("@app/views/news/view/inject1",["post"=>$post]);
  471. }else if( $attributes['type'] == 2 ){
  472. return \Yii::$app->view->render("@app/views/news/view/inject2",["post"=>$post]);
  473. }else{
  474. return \Yii::$app->view->render("@app/views/news/view/inject",["post"=>$post]);
  475. }
  476. } else {
  477. return "Битый инжект";
  478. }
  479. }
  480. }
  481. }
  482. return ArrayHelper::getValue($matches,0);
  483. },$text);
  484. return $res;
  485. }
  486. public function processGalleriesInjects(string $body)
  487. {
  488. $re = '/##_gallery-(\d+)##/mU';
  489. $res = preg_replace_callback($re,function (array $matches): string
  490. {
  491. $gallery = \app\models\front\Gallery::findOne(['id'=>ArrayHelper::getValue($matches,1)]);
  492. if($gallery instanceof \app\models\front\Gallery) {
  493. return \Yii::$app->view->render("@app/views/news/view/inject-gallery",["gallery"=>$gallery]);
  494. } else {
  495. return "";
  496. }
  497. },$body);
  498. return $res;
  499. }
  500. }