1
news.php in web/include/modules/site – MultiMag

source: web/include/modules/site/news.php @ 1a20251

Last change on this file since 1a20251 was 1a20251, checked in by Blacklight <blacklight@…>, 5 years ago
  • Реализован запрет добавления новостей без изображения
  • Property mode set to 100644
File size: 20.8 KB
Line 
1<?php
2
3//      MultiMag v0.2 - Complex sales system
4//
5//      Copyright (C) 2005-2018, BlackLight, TND Team, http://tndproject.org
6//
7//      This program is free software: you can redistribute it and/or modify
8//      it under the terms of the GNU Affero General Public License as
9//      published by the Free Software Foundation, either version 3 of the
10//      License, or (at your option) any later version.
11//
12//      This program is distributed in the hope that it will be useful,
13//      but WITHOUT ANY WARRANTY; without even the implied warranty of
14//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//      GNU Affero General Public License for more details.
16//
17//      You should have received a copy of the GNU Affero General Public License
18//      along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20namespace Modules\Site;
21
22/// Класс новостного модуля. Формирует ленты новостей. Предоставляет средства для добавления новостей и рассылки уведомлений.
23class News extends \IModule {
24
25    public function __construct() {
26        parent::__construct();
27        $this->acl_object_name = 'generic.news';
28    }
29
30    // Получить название модуля
31    /// @return Строка с именем
32    public function getName() {
33        return 'Новостная лента';
34    }
35   
36    /// Получить описание модуля
37    /// @return Строка с описанием
38    public function getDescription() {
39        return 'Просмотр, написание, и рассылка новостей'; 
40    }
41   
42    /// Запустить модуль на исполнение
43    public function run() {
44        global $tmpl;
45        $tmpl->setTitle("Новости");
46        if(!$this->ProbeRecode()) {
47            $this->ExecMode(request('mode'));
48        }
49    }
50
51    /// Проверка и исполнение recode-запроса
52    public function ProbeRecode() {
53        global $tmpl;
54        /// Обрабатывает запросы-ссылки  вида http://example.com/news/news.html
55        /// Возвращает false в случае неудачи.
56        $arr = explode('/', $_SERVER['REQUEST_URI']);
57        if (!is_array($arr))
58            return false;
59        if (count($arr) < 3)
60            return false;
61        $mode = @explode('.', $arr[2]);
62        $query = @explode('.', $arr[3]);
63        if (is_array($mode))
64            $mode = $mode[0];
65        else
66            $mode = $arr[2];
67        if (is_array($query))
68            $query = $query[0];
69        else
70            $query = $arr[3];
71        if ($mode == 'read') {
72            if ($this->isAllow())
73                $this->View($query);
74            return true;
75        }
76        else if ($mode == 'all' || $mode == 'news' || $mode == 'stocks' || $mode == 'events') {
77            if (\acl::testAccess($this->acl_object_name, \acl::CREATE, 1)) {
78                $tmpl->addContent("<a href='{$this->link_prefix}&amp;mode=add&amp;opt=$mode'>Добавить новость</a><br>");
79            }
80            if ($this->isAllow()) {
81                $tmpl->addBreadcrumb('Главная', '/');
82                $tmpl->setContent("<h1>Новости сайта</h1>");
83                $tmpl->setTitle("Новости сайта");
84                $this->ShowList($mode);
85            }
86            return true;
87        } else {
88            throw new \NotFoundException('Новость не найдена! Воспользуйтесь списком новостей.');
89        }
90        return false;
91    }
92
93    /// Отобразить страницу новостей
94    /// @param $mode список новостей
95    public function ExecMode($mode = '') {
96        global $tmpl, $CONFIG, $db;
97        $tmpl->addBreadcrumb('Главная', '/');
98        $tmpl->setContent("<h1>Новости сайта</h1>");
99        $tmpl->setTitle("Новости сайта");
100        if ($mode == '') {
101            if (\acl::testAccess($this->acl_object_name, \acl::CREATE, 1)) {
102                $tmpl->addContent("<a href='{$this->link_prefix}&amp;mode=add&amp;opt=" . request('type') . "'>Добавить новость</a><br>");
103            }
104            if ($this->isAllow()) {
105                $this->ShowList(request('type'));
106            }
107        } else if ($mode == 'read') {
108            if ($this->isAllow()) {
109                $this->View(rcvint('id'));
110            }
111        }
112        else if ($mode == 'add') {
113            if ($this->isAllow('create')) {
114                $this->WriteForm(0, request('opt'));
115            }
116        }
117        else if ($mode == 'edit') {
118            if ($this->isAllow('edit')) {
119                $id = rcvint('id');
120                $res = $db->query("SELECT `news`.`id`, `news`.`text`, `news`.`date`, `users`.`name` AS `autor_name`, `news`.`ex_date`, `news`.`img_ext`,
121                    `news`.`type`, `news`.`hidden`
122                FROM `news`
123                INNER JOIN `users` ON `users`.`id`=`news`.`autor`
124                WHERE `news`.`id`='$id'");
125                if ($res->num_rows) {
126                    $line = $res->fetch_assoc();
127                    $this->WriteForm($line['id'], $line['type'], $line['ex_date'], $line['text']);
128                }
129                else {
130                    throw new \NotFoundException('Новость не найдена.');
131                }
132            }
133        }
134        else if ($mode == 'save') {
135            if ($this->isAllow('create')) {
136                $news_id = $this->Save();
137                $this->View($news_id);
138            }
139        }
140        else if ($mode == 'pub') {
141            if ($this->isAllow('create')) {
142                $id = rcvint('id');
143                $this->Publish($id);
144            }
145        }
146        else {
147            throw new \NotFoundException("Неверный $mode");
148        }
149    }
150
151    /// Отобразить летну новостей заданного типа
152    /// @param $type - любые типы, news - только новости, stocks - только акции, events - только события
153    protected function ShowList($type = '') {
154        global $tmpl, $CONFIG, $db;
155        switch ($type) {
156            case 'news': $name = 'Новости';
157                $where = "`news`.`type`='novelty'";
158                break;
159            case 'stocks': $name = 'Акции';
160                $where = "`news`.`type`='stock'";
161                break;
162            case 'events': $name = 'События';
163                $where = "`news`.`type`='event'";
164                break;
165            default: $type = '';
166                $name = 'Новости, акции, события';
167                $where = '1';
168        }
169        if (!\acl::testAccess($this->acl_object_name, \acl::UPDATE, true)) {
170            $where .= " AND `hidden`=0";
171        }
172        $res = $db->query("SELECT `news`.`id`, `news`.`text`, `news`.`date`, `users`.`name` AS `autor_name`,
173            `news`.`ex_date`, `news`.`img_ext`, `news`.`type`, `news`.`hidden`
174        FROM `news`
175        INNER JOIN `users` ON `users`.`id`=`news`.`autor`
176        WHERE $where
177        ORDER BY `date` DESC LIMIT 50");
178        if ($res->num_rows) {
179            if($where!=1) {
180                $tmpl->addBreadcrumb('Новости, акции, события', $this->link_prefix);
181            }
182            $tmpl->addBreadcrumb($name, '');
183            $tmpl->setContent("<h1>$name</h1>");
184            $tmpl->setTitle("$name сайта");
185            if (\acl::testAccess($this->acl_object_name, \acl::CREATE, true)) {
186                $tmpl->addContent("<a href='{$this->link_prefix}&amp;mode=add&amp;opt=$type'>Добавить новость</a><br>");
187            }
188            $wikiparser = new \WikiParser();
189            while ($line = $res->fetch_assoc()) {
190                $wikiparser->title = '';
191                $tmpl->addContent("<div class='news-block'>");
192                $text = $wikiparser->parse(html_out($line['text']));
193                if ($line['img_ext']) {
194                    $miniimg = new \ImageProductor($line['id'], 'n', $line['img_ext']);
195                    $miniimg->SetX(48);
196                    $miniimg->SetY(48);
197                    $tmpl->addContent("<img src='" . $miniimg->GetURI() . "' style='float: left; margin-right: 10px;' alt=''>");
198                }
199                if ($line['type'] == 'stock') {
200                    $do = "<br><i><u>Действует до:      {$line['ex_date']}</u></i>";
201                } else if ($line['type'] == 'event') {
202                    $do = "<br><i><u>Дата проведения:   {$line['ex_date']}</u></i>";
203                } else {
204                    $do = '';
205                }
206                $link = $this->GetNewsLink($line['id']);
207                $hidden = $line['hidden'] ? '<b style="color: #f00;"> - не опубликовано</b>' : '';
208                $tmpl->addContent("<h3><a href='$link'>{$wikiparser->title}</a>$hidden</h3><p>$text<br><i>{$line['date']}, {$line['autor_name']}</i>$do</p></div>");
209                // <!--<br><a href='/forum.php'>Комментарии: 0</a>-->
210            }
211        } else {
212            throw new \NotFoundException('Новость не найдена! Воспользуйтесь списком новостей.');
213        }
214    }
215
216    /// Отобразить заданную новость
217    /// @param $id id новости, которую нужно отобразить   
218    protected function View($id) {
219        global $tmpl, $db;
220        $res = $db->query("SELECT `news`.`id`, `news`.`text`, `news`.`date`, `users`.`name` AS `autor_name`, `news`.`ex_date`, `news`.`img_ext`,
221            `news`.`type`, `news`.`hidden`
222        FROM `news`
223        INNER JOIN `users` ON `users`.`id`=`news`.`autor`
224        WHERE `news`.`id`='$id'");
225        if ($res->num_rows) {
226            $news_info = $res->fetch_assoc();
227            $edit_enable = false;
228           
229            if ($news_info['hidden']) {
230                if (!\acl::testAccess($this->acl_object_name, \acl::UPDATE, true)) {
231                    throw new \NotFoundException('Новость снята с публикации.');
232                } else {
233                    $edit_enable = true;
234                    $hidden = '<b style="color: #f00;"> - не опубликовано</b>';
235                }
236            } else {
237                $hidden = '';
238            }
239
240            $wikiparser = new \WikiParser();
241            $wikiparser->title = '';
242            $text = $wikiparser->parse(html_out($news_info['text']));
243           
244            if ($news_info['type'] == 'stock') {
245                $do = "<div id='page-info'>Действует до: {$news_info['ex_date']}</div>";
246            } else if ($news_info['type'] == 'event') {
247                $do = "<div id='page-info'>Дата проведения: {$news_info['ex_date']}</div>";
248            } else {
249                $do = '';
250            }
251            $tmpl->addBreadcrumb('Главная', '/');
252            $tmpl->addBreadcrumb('Новости', $this->link_prefix);
253            $tmpl->addBreadcrumb($wikiparser->title, '');
254            $tmpl->setContent("<h1>{$wikiparser->title}$hidden</h1>" . $do
255                . "<p>$text</p><p align='right'><i>{$news_info['date']}, {$news_info['autor_name']}</i></p>");
256            // <a href='/forum.php'>Комментарии: 0</a>
257            if($edit_enable) {
258                $tmpl->addContent("<a href='{$this->link_prefix}&amp;mode=edit&amp;id=$id'>Изменить</a><br>"
259                . "<fieldset><legend>Публикация</legend>"
260                . "<form action='{$this->link_prefix}&amp;mode=pub&amp;id=$id' method='post'>"
261                . "<label><input type='checkbox' name='send' value='1' checked>Выполнить рассылку</label><br>"
262                . "<button type='submit'>Опубликовать</button>"
263                . "</form>"
264                . "</fieldset>");
265            }
266        }
267        else {
268            throw new \NotFoundException('Новость не найдена! Воспользуйтесь списком новостей.');
269        }
270    }
271
272    /// Форма создания и редактирования новости
273    /// @param $id id новости
274    /// @param $id $type Тип новости. news - новости, stocks - акции, events -  события. По умолчанию: news
275    /// @param $ex_date Дата окончания. Не используется у новостей
276    /// @param $text Текст новости
277    protected function WriteForm($id=0, $type='news', $ex_date='', $text='') {
278        global $tmpl;
279        $novelty_c = $stock_c = $event_c = '';
280        switch ($type) {
281            case 'news':
282            case 'novelty':
283                $novelty_c = ' checked';
284                break;
285            case 'stock': 
286            case 'stocks':
287                $stock_c = ' checked';
288                break;
289            case 'event': 
290            case 'events': 
291                $event_c = ' checked';
292                break;
293        }
294        $tmpl->addBreadcrumb('Новости', $this->link_prefix);
295        if($id>0) {
296            $tmpl->addBreadcrumb('Новость N'.$id, $this->GetNewsLink($id));
297        }
298        $tmpl->addBreadcrumb('Редактирование новости', '');
299        $tmpl->addContent("
300        <form action='{$this->link_prefix}' method='post' enctype='multipart/form-data'>
301        <h2>Добавление новости</h2>
302        <input type='hidden' name='mode' value='save'>
303        <input type='hidden' name='id' value='$id'>
304        Класс новости:<br>
305        <small>Определяет место её отображения</small><br>
306        <label><input type='radio' name='type' value='novelty'$novelty_c>Обычная<br><small>Отображается только в ленте новостей</small></label><br>
307        <label><input type='radio' name='type' value='stock'$stock_c>Акция<br><small>Отображается в ленте новостей и списке акций. Дата - дата окончания акции</small></label><br>
308        <label><input type='radio' name='type' value='event'$event_c>Событие<br><small>Проведение выставки, распродажа, конурс, итд. Дата - дата наступления события</small></label><br>
309        <br>
310        Дата:<br>
311        <input type='text' name='ex_date' value='".html_out($ex_date)."'><br><br>
312        Текст новости:<br>
313        <small>Можно использовать wiki-разметку. Заголовок будет взят из текста.</small><br>
314        <textarea name='text' class='e_msg' rows='6' cols='80'>".html_out($text)."</textarea><br><br>
315        Изображение для списка новостей (jpg, png, gif):<br>
316        <small>Следите за пропорциями!</small><br>
317        <input type='hidden' name='MAX_FILE_SIZE' value='8000000'>
318        <input name='img' type='file'><br><br>
319        <button type='submit'>Записать новость</button><br>
320        <small>После записи новость нужно будет опубликовать</small>
321        </form>");
322    }
323
324    /// Сохранить новость для последующей публикации
325    protected function Save() {
326        global $tmpl, $CONFIG, $db;
327       
328        $id = rcvint('id');
329        $text = strip_tags(request('text'));
330        $type = request('type');
331        $ex_date = date("Y-m-d", strtotime(request('ex_date')));
332       
333        $wikiparser = new \WikiParser();
334        $wikiparser->parse(html_entity_decode($text, ENT_QUOTES, "UTF-8"));
335        if (!isset($wikiparser->title)) {
336            throw new Exception("Заголовок новости не задан");
337        }
338        $title = $wikiparser->title;
339       
340
341        if ($type != 'novelty' && $type != 'stock' && $type != 'event') {
342            $type = 'novelty';
343        }
344
345        $db->startTransaction();
346       
347        $data = array(
348            'type'  => $type,
349            'title' => $title,
350            'text'  => $text,
351            'autor' => $_SESSION['uid'],
352            'ex_date'=> $ex_date
353        );
354       
355        if ($id) {
356            $db->updateA('news', $id, $data);
357            $news_id = $id;
358        } else {
359            $data['hidden'] = 1;
360            $data['date'] = date("Y-m-d H:i:s");
361            $news_id = $db->insertA('news', $data);
362        }
363
364        if (!$news_id) {
365            throw new Exception("Не удалось получить ID новости");
366        }
367
368        if (is_uploaded_file($_FILES['img']['tmp_name'])) {
369            $aa = getimagesize($_FILES['img']['tmp_name']);
370            if (!$aa) {
371                throw new Exception('Полученный файл не является изображением');
372            }
373            if(!is_array($aa)) {
374                throw new Exception('Ошибка анализа заголовков изображения');
375            }
376            if (($aa[0] < 20) || ($aa[1] < 20)) {
377                throw new Exception('Слишком мальенькое изображение');
378            }
379            switch ($aa[2]) {
380                case IMAGETYPE_GIF: $ext = 'gif';
381                    break;
382                case IMAGETYPE_JPEG: $ext = 'jpg';
383                    break;
384                case IMAGETYPE_PNG: $ext = 'png';
385                    break;
386                default: throw new Exception('Формат изображения не поддерживается');
387            }
388            @mkdir($CONFIG['site']['var_data_fs'] . "/news/", 0755);
389            $m_ok = move_uploaded_file($_FILES['img']['tmp_name'], $CONFIG['site']['var_data_fs'] . "/news/$news_id.$ext");
390            if (!$m_ok) {
391                throw new Exception("Не удалось записать изображение в хранилище");
392            }
393            $db->update('news', $news_id, 'img_ext', $ext);
394        }
395        $db->commit();
396        $tmpl->msg("Новость добавлена!", "ok");
397        return $news_id;
398    }
399
400
401    /// Публикация новости
402    /// @param $id id новости
403    protected function Publish($id) {
404        global $tmpl, $db;
405        $pref = \pref::getInstance();
406        $send = request('send');
407       
408        $res = $db->query("SELECT `news`.`id`, `news`.`text`, `news`.`date`, `users`.`name` AS `autor_name`, `news`.`ex_date`, `news`.`img_ext`,
409            `news`.`type`, `news`.`hidden`
410        FROM `news`
411        INNER JOIN `users` ON `users`.`id`=`news`.`autor`
412        WHERE `news`.`id`='$id'");
413        if (!$res->num_rows) {
414            throw new \NotFoundException('Новость не найдена.');
415        }
416        $news_info = $res->fetch_assoc();
417        if(!$news_info['hidden'])   {
418            throw new \Exception('Новость уже была опубликована ранее.');
419        }
420        if(!$news_info['img_ext']) {
421            throw new \Exception('Не задано изображение новости!');
422        }
423       
424        $db->startTransaction();
425        $db->update('news', $id, 'hidden', 0);
426       
427        if($send) {
428            $wikiparser = new \WikiParser();
429            $uwtext = $wikiparser->parse(html_entity_decode($news_info['text'], ENT_QUOTES, "UTF-8"));
430            if (!isset($wikiparser->title)) {
431                throw new Exception("Заголовок новости не задан");
432            }
433            $title = $wikiparser->title;
434            $uwtext = strip_tags($uwtext);
435            $uwtext = $title . "\n" . $uwtext;
436           
437            if ($news_info['type'] == 'stock') {
438                $uwtext .= "\n\nАкция действует до: {$news_info['ex_date']}\n";
439            } else if ($news_info['type'] == 'event') {
440                $uwtext .= "\n\nСобытие пройдёт: {$news_info['ex_date']}\n";
441            }
442
443            $list_id = 'news' . $id . '.' . date("dmY") . '.' . $pref->site_name;
444            $send_res = sendSubscribe($title, $title . " - новости сайта", $uwtext, $list_id);
445            if(!$send_res) {
446                $tmpl->msg("Рассылка выполнена успешно.", "ok");           
447            } else {
448                $txt = "При рассылке произошли ошибки:<br>";
449                foreach($send_res as $v) {
450                    $txt.=$v."<br>";
451                }
452                $tmpl->errorMessage($txt);           
453            }
454        }
455        $db->commit();
456        $tmpl->msg("Новость опубликована!", "ok");
457    }
458
459    /// Получить ссылку на новость с заданным ID
460    /// @param $id id новости
461    /// @param $alt_param Дополнительные параметры в ссылке
462    protected function GetNewsLink($id, $alt_param = '') {
463        global $CONFIG;
464        if ($CONFIG['site']['rewrite_enable']) {
465            return "/news/read/$id.html" . ($alt_param ? "?$alt_param" : '');
466        } else {
467            return "{$this->link_prefix}&amp;mode=read&amp;id=$id" . ($alt_param ? "&amp;$alt_param" : '');
468        }
469    }
470
471}
Note: See TracBrowser for help on using the repository browser.