News.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. <?php
  2. namespace app\models;
  3. use app\helpers\Transliterator;
  4. use app\models\base\Comments;
  5. use app\models\base\Gallery;
  6. use app\models\base\Image;
  7. use app\models\base\OldNews;
  8. use DOMDocument;
  9. use yii\db\ActiveQuery;
  10. use yii\helpers\ArrayHelper;
  11. use yii\helpers\Inflector;
  12. use yii;
  13. use app\components\behaviors\CachedBehavior;
  14. use yii\helpers\Url;
  15. /**
  16. * @property string $url
  17. * @property $mainPic
  18. * @property Image $preview
  19. * @property Image $image
  20. * @property OldNews $old
  21. * @property string $publishedAt
  22. * @property Comments[] $commentsAll
  23. * @property Gallery[] $galleries
  24. */
  25. class News extends \app\models\base\News
  26. {
  27. /*
  28. * Ключи кэширования для авто сброса
  29. */
  30. public static $keysCache=[
  31. 'MainPageBlock' => 120,
  32. 'main_page_lenta' =>120,
  33. 'main-page-main-view-glob' => 60, //600
  34. 'main-page-main-view' => 60,
  35. 'topic-news-##' => 3600
  36. ];
  37. private static function processPersons(string $body)
  38. {
  39. $re = '/<a.*class=[\'"](person|person-inject)[\'"].*href=[\'"]\/person\/(.+)[\'"].*>(\W+)<\/a>/m';
  40. $res = preg_replace_callback($re,function (array $matches): string
  41. {
  42. $person = Person::find()->andWhere(['alias'=>ArrayHelper::getValue($matches,'2')])->one();
  43. if($person instanceof Person){
  44. switch (ArrayHelper::getValue($matches,'1')) {
  45. case "person-inject":
  46. return \Yii::$app->view->render("/news/view/inject-person-as-widget", ["person" => $person]);
  47. break;
  48. default:
  49. return \Yii::$app->view->render("/news/view/inject-person-as-link", ["person" => $person]);
  50. }
  51. }
  52. return ArrayHelper::getValue($matches,0);
  53. }, $body);
  54. return $res;
  55. }
  56. private static function processBody($body,$model)
  57. {
  58. $counter = 0;
  59. $re = '/<(?:p|h2).*>(.*)<\/(?:p|h2)>/msU';
  60. $res = preg_replace_callback($re,function (array $matches) use(&$counter,$model): string
  61. {
  62. $counter++;
  63. if($counter==2 && Yii::$app->deviceDetect->isMobile() && !$model->inTopic(158)) return $matches[0].Yii::$app->controller->renderPartial("@app/views/_etc/banners/adfinityInContent");
  64. if($counter==3 && !Yii::$app->deviceDetect->isMobile() && !$model->inTopic(158)) return $matches[0].Yii::$app->controller->renderPartial("@app/views/_etc/banners/desktopInContent");
  65. return $matches[0];
  66. },$body);
  67. return $res;
  68. }
  69. private static function processGalleriesInjects(string $body, News $post)
  70. {
  71. $re = '/##_gallery-(\d+)##/mU';
  72. $res = preg_replace_callback($re,function (array $matches) use($post): string
  73. {
  74. $gallery = \app\models\front\Gallery::findOne(['id'=>ArrayHelper::getValue($matches,1)]);
  75. if($gallery instanceof \app\models\front\Gallery) {
  76. return \Yii::$app->view->render("/news/view/inject-gallery-x",["gallery"=>$gallery, "model"=>$post]);
  77. } else {
  78. return "";
  79. }
  80. },$body);
  81. return $res;
  82. }
  83. public function getUrl($full = false):string
  84. {
  85. if(count($this->topics)>0){
  86. $topic = Transliterator::toUrl($this->topics[0]->title)."/";
  87. } else{
  88. $topic = "";
  89. }
  90. switch ($this->type){
  91. // case 6:
  92. // return "https://old.amic.ru/long/{$this->id}/";
  93. // break;
  94. default:
  95. $base = Url::base('https');
  96. $alias = trim( $this->alias );
  97. if( $alias == '' ){
  98. $alias = Transliterator::toUrl($this->title);
  99. }
  100. if( isset( $this->dt_pub ) && strtotime( $this->dt_pub ) > strtotime('2023-01-27 15:00:00') ){
  101. return ($full ? $base : "")."/news/".$alias."-".$this->id;
  102. }
  103. return ($full ? $base : "")."/news/".$topic.$alias."-".$this->id;
  104. }
  105. }
  106. public function getMainPic(){
  107. }
  108. /**
  109. * @return Image
  110. */
  111. public function getPreview($type="webp"):Image{
  112. return Image::findOne($this->id,$type,$this->photo_name);
  113. }
  114. public function getOld():ActiveQuery
  115. {
  116. return $this->hasOne(OldNews::class,['alias'=>'alias']);
  117. }
  118. /**
  119. * @return ActiveQuery
  120. */
  121. public function getCommentsAll(): ActiveQuery
  122. {
  123. if($this->comments=="Y"){
  124. return $this->hasMany(Comments::class,['news_id'=>"id"])->andWhere(['visible'=>'Y']);
  125. }
  126. return $this->hasMany(Comments::class,['news_id'=>"id"])->andWhere(0);
  127. }
  128. public static function getMainView(){
  129. return self::find()->joinWith("topics t")->andWhere(["t.id"=>[33,]]);
  130. }
  131. public static function getNH(){
  132. return self::find()->andWhere(['active'=>'Y', 'NH'=>['Y','F']])->andWhere(['between', 'dt_pub', date("Y-m-d H:i:00",time()-2678400*3 ) , date("Y-m-d H:i:00")])->orderBy(["NH"=>SORT_DESC,"dt_pub"=>SORT_DESC])->limit(1);
  133. }
  134. public static function getPartnersNews(){
  135. return self::find()->joinWith("topics t")->andWhere(["t.id"=>[34,]]);
  136. }
  137. public static function getMainOfWeek(){
  138. return self::find()->joinWith("topics t")->andWhere(["t.id"=>[35,]]);
  139. }
  140. public function getPublishedAt(){
  141. if($this->dt_pub<date("Y-m-d H:i:s",strtotime("-1 day"))){
  142. return
  143. date("d",strtotime($this->dt_pub))." ".mb_strtolower(Transliterator::month(date("n",strtotime($this->dt_pub))))
  144. .date(" Y, H:i", strtotime($this->dt_pub))
  145. ;
  146. } else {
  147. $diff = ceil((time() - strtotime($this->dt_pub))/60); //В минутах
  148. if($diff<=60){
  149. return Transliterator::plural($diff,['минуту','минуты', 'минут'],true,'только что','минуту')." назад";
  150. } else {
  151. $diff = (int)floor($diff/60);
  152. return Transliterator::plural($diff,['час','часа', 'часов'],true,'только что','час')." назад";
  153. }
  154. }
  155. }
  156. public function getPublishedNorm(){
  157. return
  158. date("d",strtotime($this->dt_pub))." ".mb_strtolower(Transliterator::month(date("n",strtotime($this->dt_pub))))
  159. .date(" Y, H:i", strtotime($this->dt_pub))
  160. ;
  161. }
  162. public function renderBody()
  163. {
  164. $post = $this;
  165. $device = Yii::$app->deviceDetect->isMobile()?"mobile":"desktop";
  166. return \Yii::$app->cache->getOrSet("post-body8-".$this->id."-".$device,function () use ($post){
  167. $body = $post->text;
  168. //Нужно воткнуть рекламу после второго абзаца
  169. $body = self::processBody($body,$this);
  170. // $body = self::processSingleImg($body);
  171. $body = self::processTextImg($body);
  172. $body = self::processInjects($body);
  173. $body = self::processAudio($body);
  174. $body = self::processSlider($body, $post); //old slider
  175. $body = self::processGalleriesInjects($body, $post);
  176. $body = self::processYoutube($body);
  177. $body = self::processIframe($body);
  178. $body = self::processSpecialFormats($body);
  179. $body = self::processPersons($body);
  180. $body = self::processTest($body);
  181. $body = self::ShowPollsWidget($body);
  182. $body = self::ShowPreportWidget($body);
  183. if( $post->inscription == 0 ) $body = self::processAhref($body); // кроме комерческих
  184. return $body;
  185. },YII_ENV_DEV?1:60);
  186. }
  187. public static function processTest($text)
  188. {
  189. $text = str_replace("\xc2\xa0", ' ', $text);
  190. $text = str_replace(['<p>&nbsp;</p>','<p> </p>'], "\c", $text);
  191. $text = trim( $text );
  192. $text = str_replace( "\c", "<p></p>", $text);
  193. return $text;
  194. }
  195. public static function processSpecialFormats($text)
  196. {
  197. if( stripos($text, 'class="juxtapose"') !== false ){
  198. $str = '
  199. <script src="/js/juxtapose/juxtapose.min.js"></script>
  200. <link rel="stylesheet" href="/js/juxtapose/juxtapose.css">
  201. ';
  202. return $text.$str;
  203. }
  204. return $text;
  205. }
  206. /*
  207. * Парсинг для старых слайдеров (очень старых)
  208. */
  209. public static function processSlider($text, $obj)
  210. {
  211. if( $obj->uid == '' ) return $text;
  212. $dir = '/images/items/'.$obj->uid;
  213. $files1 = @scandir(Yii::getAlias('@webroot').$dir.'/thumbnails/',0);
  214. $old_title = ( trim($obj->old_gallery_title) != '' )?''.trim($obj->old_gallery_title).'':'';
  215. if( $files1 == false || count( $files1 ) < 4){
  216. $files1 = @scandir(Yii::getAlias('@webroot').$dir.'/images/thumbnails/', 0);
  217. if( $files1 === false || count( $files1 ) == 0){
  218. return $text;
  219. }else{
  220. $i=1;
  221. ob_start();
  222. foreach( $files1 as $file ){
  223. if( preg_match('/^.*\.(gif|jpe?g|png)/i', $file ) ){
  224. $str = @file_get_contents( Yii::getAlias('@webroot').$dir.'/info/'.$file.'.json' );
  225. if( $str == false ){
  226. $str='{"title":"","copyrate":""}';
  227. }
  228. $js_title = json_decode( $str );
  229. $str_title = ( $js_title->title == '' )?$old_title:$js_title->title;//iconv('UTF-8','cp1251',$js_title->title);
  230. $str_title .= ( $js_title->copyrate == '' )?'':'<br><span class="style8">'.$js_title->copyrate.'</span>';
  231. $add_cap = ($str_title != '')?", caption: '".$str_title."'":'';
  232. ?>
  233. {img: '<?=$dir?>/images/<?=$file?>', thumb: '<?=$dir?>/images/thumbnails/<?=$file?>' <?=$add_cap?>},
  234. <?
  235. }
  236. }
  237. $ltext = ob_get_contents();
  238. ob_end_clean();
  239. }
  240. }else{
  241. $i=1;
  242. ob_start();
  243. foreach( $files1 as $file ){
  244. if( preg_match('/^.*\.(gif|jpe?g|png)/i', $file ) ){
  245. ?>
  246. {img: '<?=$dir?>/big/<?=$file?>', thumb: '<?=$dir?>/thumbnails/<?=$file?>'},
  247. <?
  248. }
  249. }
  250. $ltext = ob_get_contents();
  251. ob_end_clean();
  252. }
  253. ob_start();
  254. if( isset( $ltext ) ){
  255. if( strstr( $text, 'id="mycarousel"') === false ){
  256. echo '<div id="mycarousel"></div>';
  257. }
  258. ?>
  259. <!-- Fotorama -->
  260. <?
  261. $c = Yii::$app->acache;
  262. $c->set('fotorama', true);
  263. ?>
  264. <!-- <div class="fotorama" data-width="700" data-ratio="700/467" data-max-width="100%"></div>-->
  265. <script type="text/javascript">
  266. window.addEventListener("DOMContentLoaded",function () {
  267. $('#mycarousel').fotorama({ width:'100%',maxwidth:780,thumbheight:70,thumbwidth:94, captions: true,nav:'thumbs', caption: 'overlay', allowfullscreen:'native', data: [ <?=$ltext?> ] });
  268. });
  269. </script>
  270. <?
  271. }
  272. $ltext = ob_get_clean();
  273. return $ltext.$text;
  274. }
  275. public static function processAudio($text)
  276. {
  277. $re = '/<a([^>]+)href=\"([^>]*\/files\/)(.+)\.mp3\"([^>]*)>([^<]*)<\/a>/i';
  278. $res = preg_replace_callback($re,function (array $matches): string
  279. {
  280. $title = isset($matches[5])?$matches[5]:'';
  281. if( !strstr( $matches[1], "download") ) {
  282. $url = $matches[2];
  283. $audio = '<source src="'.$url.$matches[3].'.mp3" type="audio/mpeg">'."\n";
  284. if( file_exists( $_SERVER['DOCUMENT_ROOT'].''.$matches[2].$matches[3].'.mp3.ogg' ) ){
  285. $audio .= '<source src="'.$url.$matches[3].'.mp3.ogg" type="audio/ogg; codecs=vorbis">'."\n";
  286. }
  287. return \Yii::$app->view->render("/news/view/audio",["audio"=>$audio, "title"=>$title]);
  288. } else {
  289. return ArrayHelper::getValue($matches,0);
  290. }
  291. },$text);
  292. return $res;
  293. }
  294. /*
  295. * Спрятать внешнии ссылки от поисковиков
  296. * Псевдо SEO
  297. *
  298. */
  299. public static function processAhref($text)
  300. {
  301. $re = '/<a[^>]+href=\"(https?:\/\/[^"]*)\".*>/Ui';
  302. $res = preg_replace_callback($re,function (array $matches): string
  303. {
  304. $mydomain = \Yii::$app->params['mydomain'];
  305. foreach( $mydomain as $find ){
  306. if( stripos( $matches[1], $find) !== false ) return $matches[0];
  307. }
  308. $ret = str_replace( $matches[1], "/go/?u=".urlencode($matches[1]), $matches[0] );
  309. return $ret;
  310. },$text);
  311. return $res;
  312. }
  313. public static function processInjects($text)
  314. {
  315. $re = '/##news_(.*)##/mU';
  316. $res = preg_replace_callback($re,function (array $matches): string
  317. {
  318. $post = News::findOne(['uid'=>ArrayHelper::getValue($matches,1)]);
  319. if($post instanceof News) {
  320. return \Yii::$app->view->render("/news/view/inject",["post"=>$post]);
  321. } else {
  322. return "";
  323. }
  324. },$text);
  325. if( strlen($res) != strlen($text) ) return $res;
  326. $re = "|<div[^>]+>.[^<]*</div>|U";
  327. $res = preg_replace_callback($re,function (array $matches): string
  328. {
  329. @$xml = simplexml_load_string( str_replace( '&nbsp;', '', ArrayHelper::getValue($matches,0) ), 'SimpleXMLElement', LIBXML_NOCDATA );
  330. if( $xml ){
  331. $attributes =$xml->attributes();
  332. if( $attributes['class'] == 'insinject' || $attributes['id'] == "inject" ){
  333. $r = preg_match( "/^https?:\/\/(.*)\/(.*)\/(\d+)\/$/i", $attributes['url'], $aa );
  334. $id = 0;
  335. if( $r && ($aa[1] == 'www.amic.ru' || $aa[1] == Yii::$app->request->serverName) && $aa[3]*1 > 0 ){
  336. // old style inject
  337. $id = $aa[3]*1;
  338. }
  339. $r = preg_match( "/^https?:\/\/(.*)\/.*(\d+)\/?$/Ui", $attributes['url'], $aa );
  340. if( $r && ($aa[1] == Yii::$app->request->serverName || $aa[1] == 'www.amic.ru')&& $aa[2]*1 > 0 ){
  341. // new style inject
  342. $id = $aa[2]*1;
  343. }
  344. $r = preg_match( '/^https?:\/\/(.*)\/person\/(.*)\/?$/Ui', $attributes['url'], $aa );
  345. if( $r && ($aa[1] == Yii::$app->request->serverName || $aa[1] == 'www.amic.ru')&& $aa[2] != '' ){
  346. $person = \app\models\Person::findOne(['alias'=>$aa[2]]);
  347. if( ( !$person instanceof Person ) || $person->show == 'N' ){
  348. return "";
  349. }
  350. return \Yii::$app->view->render("/news/view/injectp",["post"=>$person]);
  351. }
  352. if( $id ){
  353. //$attributes['type'] to do
  354. $post = News::findOne(['id'=>$id]);
  355. if($post instanceof News) {
  356. if( $attributes['type'] == 1 ){
  357. return \Yii::$app->view->render("/news/view/inject1",["post"=>$post]);
  358. }else if( $attributes['type'] == 2 ){
  359. return \Yii::$app->view->render("/news/view/inject2",["post"=>$post]);
  360. }else{
  361. return \Yii::$app->view->render("/news/view/inject",["post"=>$post]);
  362. }
  363. } else {
  364. return "Битый инжект";
  365. }
  366. }
  367. }
  368. }
  369. return ArrayHelper::getValue($matches,0);
  370. },$text);
  371. return $res;
  372. }
  373. public static function processIframe($text) //https://youtu.be/sfzX5fMaSj4 -> https://www.youtube.com/embed/sfzX5fMaSj4
  374. {
  375. $re = '/(<iframe[^>]*>).*(<\/iframe>)/Ui';
  376. $text = preg_replace_callback($re,function (array $mt): string
  377. {
  378. //print_r($mt);
  379. // libxml_use_internal_errors(true);
  380. $content = $mt[0];
  381. $content = str_replace( '&', '#-#', $content);
  382. $HXML='<?xml version="1.0" encoding="UTF-8"?>';
  383. $doc = new DOMDocument('1.0','UTF-8'); // нормализация xml
  384. @$doc->loadHTML($HXML.$content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOCDATA);
  385. // $doc->encoding = 'UTF-8';
  386. $doc->normalizeDocument();
  387. $content = $doc->saveXML($doc->documentElement);
  388. $content = html_entity_decode($content);
  389. $content = str_replace( '<?xml version="1.0" standalone="yes"?>', '', $content);
  390. $content = str_replace( '<?xml version="1.0" encoding="UTF-8"??>', '', $content);
  391. $content = str_replace( '<?xml version="1.0" encoding="UTF-8"?>', '', $content);
  392. $xml = simplexml_load_string( $content, 'SimpleXMLElement',LIBXML_HTML_NODEFDTD | LIBXML_NOXMLDECL |LIBXML_HTML_NOIMPLIED );
  393. if( $xml ){
  394. $attrs = $attributes =$xml->attributes();
  395. //print_r( $attrs );
  396. $xmlo = simplexml_load_string( $HXML.'<iframe loading="lazy"> </iframe>', 'SimpleXMLElement', LIBXML_NOCDATA );
  397. // var_dump($attrs);
  398. if( $attrs['src'] && strpos( $attrs['src'], 'vk.com' ) !== false ) $fixedRatio = '3/2';
  399. foreach( $attrs as $key=>$attr ){
  400. if( $key == 'height'){
  401. $height = preg_replace('/[\D]/', '', $attr);
  402. if( $height == 0 ) $height = 0.1;
  403. $attr = 'auto';
  404. }
  405. if( $key == 'width'){
  406. $width = preg_replace('/[\D]/', '', $attr);
  407. if( strpos($attr, '%') !== false ){
  408. $width = $width*720/100;
  409. }
  410. $attr = '100%';
  411. }
  412. if( isset($height) && isset($width) ){
  413. if( isset( $fixedRatio ) ){
  414. $ratio = $fixedRatio;
  415. }else{
  416. $ratio = round( $width/$height, 2 );
  417. }
  418. if( !isset($xmlo->attributes()->style) ){
  419. $xmlo->addAttribute('style', 'width:100%; aspect-ratio:'.$ratio);
  420. }else{
  421. $xmlo->attributes()->style = $xmlo->attributes()->style.'width:100%; aspect-ratio:'.$ratio;
  422. }
  423. unset($height);
  424. unset($width);
  425. }
  426. @$xmlo->addAttribute($key, $attr);
  427. }
  428. return str_replace( '#-#', '&', preg_replace('|^\<\?xml version="1.0" encoding="UTF-8"\?\>\n|i', '',$xmlo->asXML()) );
  429. }
  430. return $mt[0];
  431. }, $text);
  432. return $text;
  433. }
  434. /*
  435. * Парсинг короткой ссылки ютуба
  436. *
  437. */
  438. public static function processYoutube($text) //https://youtu.be/sfzX5fMaSj4 -> https://www.youtube.com/embed/sfzX5fMaSj4
  439. {
  440. return str_replace( 'https://youtu.be/', 'https://www.youtube.com/embed/', $text );
  441. }
  442. public static function processSingleImg($text)
  443. {
  444. $re = '/(<[p|a][^>]*>)(<img.*>)(<\/[p|a]>)/i';
  445. $res = preg_replace_callback($re,function (array $matches): string
  446. {
  447. $img = ArrayHelper::getValue($matches,2);
  448. $a = array();
  449. $title = '';
  450. if( preg_match( '/title="(.*)"/mU', $img, $a) ){
  451. $title = '<div class="image-title" style="margin-top: 20px;">'.$a[1].'</div>';
  452. }
  453. if( $title && preg_match( '/rel-credit="(.*)"/mU', $img, $a) ){
  454. if( trim( $a[1] ) != '' ){
  455. $title = str_replace('</div>', ' фото: '.$a[1].'</div>', $title);
  456. }
  457. }
  458. if( $title && preg_match( '/longdesc="(.*)"/mU', $img, $a) ){
  459. $title = str_replace('</div>', ' <a href="'.$a[1].'">'.$a[1].'</a></div>', $title);
  460. }
  461. $style = '';
  462. $a = array();
  463. if( preg_match( '/style="(.*)"/mU', $img, $a) ){
  464. $style = $a[1];
  465. $style = str_replace('height', 'data-height', $style);
  466. $img = str_replace('margin', 'margin-block', $img);
  467. }
  468. //class='picture-cont-16x9'
  469. $pic= "<div style='{$style}' class=\"pic\">
  470. <picture class='w-100'>
  471. {$img}
  472. </picture>
  473. {$title}
  474. </div>
  475. ";
  476. return str_replace('<p','<div',ArrayHelper::getValue($matches,1)).$pic.str_replace('p>','div>',ArrayHelper::getValue($matches,3));
  477. },$text);
  478. return $res;
  479. }
  480. public static function processTextImg($text)
  481. {
  482. $text = str_replace('src="http://', 'src="https://', $text); //в качестве бреда
  483. $text = str_replace( 'publib/gimage.php?image=/', '', $text ); // очень очень старый движ
  484. $re = '/(<img[^>]*>)/i';
  485. $text = preg_replace_callback($re,function (array $mt): string
  486. {
  487. //print_r($mt);
  488. libxml_use_internal_errors(true);
  489. $staic = false;
  490. $content = $mt[0];
  491. $content = str_replace('&','$%$',$content);
  492. $HXML='<?xml version="1.0" encoding="UTF-8"?>';
  493. $doc = new DOMDocument('1.0','UTF-8'); // нормализация xml
  494. $doc->loadHTML($HXML.$content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOCDATA);
  495. // $doc->encoding = 'UTF-8';
  496. $doc->normalizeDocument();
  497. $content = $doc->saveXML($doc->documentElement);
  498. $content = html_entity_decode($content);
  499. $content = str_replace( '<?xml version="1.0" standalone="yes"?>', '', $content);
  500. $content = str_replace( '<?xml version="1.0" encoding="UTF-8"??>', '', $content);
  501. $content = str_replace( '<?xml version="1.0" encoding="UTF-8"?>', '', $content);
  502. $xml = simplexml_load_string( $content, 'SimpleXMLElement',LIBXML_HTML_NODEFDTD | LIBXML_NOXMLDECL |LIBXML_HTML_NOIMPLIED );
  503. if( $xml ){
  504. $attrs = $attributes =$xml->attributes();
  505. //print_r( $attrs );
  506. $xmlo = simplexml_load_string( $HXML.'<span><picture class="w-100"><img /></picture></span>', 'SimpleXMLElement', LIBXML_NOCDATA );
  507. // var_dump($attrs);
  508. $url = isset($attrs->longdesc)?$attrs->longdesc:'';
  509. foreach( $attrs as $key=>$attr ){
  510. if( $key == 'height'){
  511. $xmlo->addAttribute('data-height', $attr);
  512. continue;
  513. }
  514. if( $key == 'style'){
  515. $attr = trim( $attr, " ;\r\n" );
  516. //$attr = str_replace(' ', '', $attr);
  517. $css = explode( ";", $attr );
  518. $stylesd = array();
  519. $stylesi = array();
  520. foreach( $css as $attrx ){
  521. list($key1, $value) = explode( ":", $attrx );
  522. $key1 = trim($key1);
  523. if( $key1 == 'height' || $key1 == '' ){
  524. continue;
  525. }elseif( stristr( 'margin', $key1 ) !== false ){
  526. $stylesd[] = "{$key1}:{$value}";
  527. }elseif( $key1 == 'float' ){
  528. $stylesd[] = "{$key1}:{$value}";
  529. }elseif( $key1 == 'width' ){
  530. //$value = preg_replace("/[^0-9\-.]/", '', $value);
  531. if( preg_replace("/[^0-9\-.]/", '', $value) > 720-150 ){
  532. $value = '100%';
  533. }
  534. $stylesd[] = "{$key1}:{$value}";
  535. $stylesi[] = "{$key1}:100%";
  536. $stylesd[] = "display:block";
  537. }elseif( $key1 && $value){
  538. $stylesd[] = "{$key1}:{$value}";
  539. if( $key1 == 'margin-right' || $key1 == 'margin-left' ) $value = 0;
  540. if( $key1 != 'margin' ) {
  541. $stylesi[] = "{$key1}:{$value}";
  542. }
  543. }
  544. }
  545. $attr = implode('; ', $stylesd);
  546. $xmlo->addAttribute($key, $attr);
  547. $attr = implode('; ', $stylesi);
  548. $xmlo->picture->img->addAttribute($key, $attr);
  549. }
  550. if( $key == 'class'){
  551. if( $attr == 'staic' ){
  552. $staic = true;
  553. }
  554. }
  555. $title = '';
  556. if( $key == 'title'){
  557. $title = str_replace( '"', "'", trim($attr));
  558. $xmlo->picture->img->addAttribute($key, $title);
  559. /*
  560. if( isset( $attrs['rel-credit'] ) && trim($attrs->rel-credit) != ''){
  561. $attr = trim($attrs->{rel-credit})?$attrs->rel-credit:'';
  562. $title .= ' фото: '.$attr;
  563. }
  564. */
  565. }else{
  566. if( $xmlo->picture->img->$key && trim($key) != ''){
  567. $xmlo->picture->img->$key = $attr;
  568. }else{
  569. if( trim($key) != '' ) @$xmlo->picture->img->addAttribute($key, $attr);
  570. }
  571. }
  572. if( $title ){
  573. $e = $xmlo->addChild('span', $title); // class="image-title" style="margin-top: 20px;"
  574. $e->addAttribute('class',"image-title image-title-fix");
  575. //$e->addAttribute('style',"margin-top: 20px;");
  576. if( $url ){
  577. $e1 = $e->addChild('a', $url);
  578. $e1->addAttribute('href', $url);
  579. }
  580. }
  581. }
  582. if( isset( $xmlo->attributes()->style ) ){
  583. $s = $xmlo->attributes()->style .= ';overflow: hidden;';
  584. unset($xmlo->attributes()->style);
  585. $xmlo->addAttribute('style',$s );
  586. }else{
  587. $xmlo->addAttribute('style', 'overflow: hidden;');
  588. }
  589. $str = str_replace('$%$','&',preg_replace('|^\<\?xml version="1.0" encoding="UTF-8"\?\>\n|i', '',$xmlo->asXML()));
  590. if( $staic ){
  591. $str = str_replace( '100%', 'auto', $str);
  592. }
  593. return $str;
  594. }
  595. return $mt[0];
  596. }, $text);
  597. return $text;
  598. }
  599. public function behaviors()
  600. {
  601. $keys = array_keys(self::$keysCache);
  602. return [
  603. 'CachedBehavior' => [
  604. 'class' => CachedBehavior::class,
  605. 'cache_key' => $keys,
  606. ]
  607. ];
  608. }
  609. public static function find0()
  610. {
  611. return parent::find();
  612. }
  613. public static function find()
  614. {
  615. return parent::find()->orderBy(["dt_pub"=>SORT_DESC]);
  616. }
  617. /**
  618. * @return ActiveQuery
  619. */
  620. public function getGalleries()
  621. {
  622. return $this->hasMany(Gallery::class,['post_id'=>'id']);
  623. }
  624. public function getYoutubeEmbedLink(){
  625. $data = parse_url($this->embed_url);
  626. if(is_array($data)){
  627. switch (ArrayHelper::getValue($data,'path')){
  628. case "/watch":
  629. $re = '/v=(.*)/m';
  630. preg_match_all($re, ArrayHelper::getValue($data,'query'), $matches, PREG_SET_ORDER, 0);
  631. $video_id = '/'.ArrayHelper::getValue($matches,'0.1');
  632. break;
  633. default:
  634. $video_id = ArrayHelper::getValue($data,'path','');
  635. }
  636. return "https://www.youtube.com/embed".$video_id;
  637. }
  638. return "";
  639. }
  640. public function inTopic(int $topic_id): bool
  641. {
  642. return (bool)$this->getTopicRelations()->andWhere(['topic_id'=>$topic_id])->count();
  643. }
  644. public static function ShowPollsWidget( $str ){
  645. preg_match_all( "|<div[^>]+>.[^<]*</div>|U", $str, $a, PREG_SET_ORDER );
  646. foreach ( $a as $item ){
  647. $xml = @simplexml_load_string( str_replace( '&nbsp;', '', $item[0] ), 'SimpleXMLElement', LIBXML_NOCDATA );
  648. if( $xml ){
  649. $attributes =$xml->attributes();
  650. if( $attributes['id'] == 'widgetpolls' ){
  651. // старый стиль url
  652. $r = preg_match( "/^https?:\/\/(.*)\/(.*)\/widget\/(\d+)\/$/i", trim($attributes['url']), $aa );
  653. if( $r && $aa[3]*1 > 0 ){
  654. if( $aa[2] == 'polls' ){
  655. $str = str_replace( $item[0], self::ShowPollsHtml( str_replace( ['http://www.amic.ru'],[''],$attributes['url']), $attributes['type'] ), $str );
  656. }
  657. }else{
  658. $r = preg_match( "/^https?:\/\/(.*)\/inquirer\/(\d+)$/i", trim($attributes['url']), $aa );
  659. if( $r && $aa[2]*1 > 0 ){
  660. $str = str_replace( $item[0], self::ShowPollsHtml( "/polls/widget/".$aa[2]*1, $attributes['type'] ), $str );
  661. }
  662. }
  663. }
  664. }
  665. }
  666. return $str;
  667. }
  668. public static function ShowPollsHtml( $url, $type ){
  669. static $i = 0;
  670. $url = trim($url);
  671. ob_start();
  672. ?>
  673. <script type="text/javascript">
  674. function AdjustIframeHeightOnLoad() { document.getElementById("form-iframe").style.height = document.getElementById("form-iframe").contentWindow.document.body.scrollHeight + "px"; }
  675. function AdjustIframeHeight(i) { document.getElementById("form-iframe").style.height = parseInt(i) + "px"; }
  676. </script>
  677. <div class="shadow p-2 mb-4 rounded" style="width:100%">
  678. <div class="header"></div>
  679. <iframe id="form-iframe" onload="AdjustIframeHeightOnLoad()" scrolling="no" src="<?=$url.'?widget='.$type?>" style="margin:0; width:100%; height:400px; border:none; overflow:hidden;"></iframe></div>
  680. <?
  681. $i++;
  682. return ob_get_clean();
  683. }
  684. public static function ShowPreportWidget( $str ){
  685. preg_match_all( "|<div[^>]+>.[^<]*</div>|U", $str, $a, PREG_SET_ORDER );
  686. foreach ( $a as $item ){
  687. $xml = @simplexml_load_string( str_replace( '&nbsp;', '', $item[0] ), 'SimpleXMLElement', LIBXML_NOCDATA );
  688. if( $xml ){
  689. $attributes =$xml->attributes();
  690. if( $attributes['id'] == 'widgetgallery' ){
  691. // старый стиль url
  692. $r = preg_match( "/^https?:\/\/(.*)\/(.*)\/(\d+)\/$/i", trim($attributes['url']), $aa );
  693. if( $r && $aa[3]*1 > 0 ){
  694. if( $aa[2] == 'photo' ){
  695. $str = str_replace( $item[0], self::ShowPreportHtml( "/photo/widget/".$aa[3]*1, $attributes['type'] ), $str );
  696. $c = Yii::$app->acache;
  697. $c->set('fotorama', true);
  698. }
  699. }
  700. }
  701. }
  702. }
  703. return $str;
  704. }
  705. public static function ShowPreportHtml( $url, $type ){
  706. static $i = 0;
  707. ob_start();
  708. ?>
  709. <div id='galleryID<?=$i?>'></div>
  710. <script>window.addEventListener("DOMContentLoaded",function () {$( '#galleryID<?=$i?>' ).load( '<?=$url?>'+'?widget=<?=$type?>');});</script>
  711. <?
  712. $i++;
  713. return ob_get_clean();
  714. }
  715. }