source: trunk/Modules/News/News.php

Last change on this file was 964, checked in by chronos, 4 months ago
  • Fixed: Correct filtering of RSS news.
File size: 13.1 KB
Line 
1<?php
2
3include_once(dirname(__FILE__).'/NewsPage.php');
4include_once(dirname(__FILE__).'/NewsSource.php');
5include_once(dirname(__FILE__).'/Import/Vismo.php');
6include_once(dirname(__FILE__).'/Import/ZdechovNET.php');
7
8function CategoryItemCompare($Item1, $Item2)
9{
10 if ($Item1['Index'] == $Item2['Index']) return 0;
11 return $Item1['Index'] > $Item2['Index'] ? -1 : 1;
12}
13
14class ModuleNews extends Module
15{
16 public int $NewsCountPerCategory = 3;
17 public string $UploadedFilesFolder = 'files/news/';
18 public array $NewsSetting;
19
20 function __construct(System $System)
21 {
22 parent::__construct($System);
23 $this->Name = 'News';
24 $this->Version = '1.0';
25 $this->Creator = 'Chronos';
26 $this->License = 'GNU/GPLv3';
27 $this->Description = 'News and news groups management';
28 $this->Dependencies = array(ModuleUser::GetName(), ModuleLog::GetName(), ModuleFile::GetName());
29 $this->Models = array(NewsCategory::GetClassName(), News::GetClassName());
30 }
31
32 function DoStart(): void
33 {
34 $this->System->RegisterPage(['aktuality'], 'PageNews');
35 $this->System->RegisterPage(['aktuality', 'subscription'], 'PageNewsSubscription');
36 $this->System->RegisterPage(['aktuality', 'rss'], 'PageNewsRss');
37 $this->System->RegisterPage(['aktuality', 'aktualizace'], 'PageNewsUpdate');
38 Core::Cast($this->System)->FormManager->RegisterClass('News', array(
39 'Title' => 'Aktualita',
40 'Table' => 'News',
41 'DefaultSortColumn' => 'Date',
42 'DefaultSortOrder' => 1,
43 'Items' => array(
44 'Category' => array('Type' => 'TNewsCategory', 'Caption' => 'Kategorie', 'Default' => 0),
45 'Title' => array('Type' => 'String', 'Caption' => 'Nadpis', 'Default' => ''),
46 'Content' => array('Type' => 'Text', 'Caption' => 'Obsah', 'Default' => ''),
47 'Date' => array('Type' => 'Date', 'Caption' => 'Datum', 'Default' => ''),
48 'Author' => array('Type' => 'String', 'Caption' => 'Autor', 'Default' => ''),
49 'Enclosure' => array('Type' => 'String', 'Caption' => 'Přílohy', 'Default' => ''),
50 'User' => array('Type' => 'TUser', 'Caption' => 'Uživatel', 'Default' => ''),
51 'IP' => array('Type' => 'IPv4Address', 'Caption' => 'IP adresa', 'Default' => '', 'ReadOnly' => true),
52 'Link' => array('Type' => 'Hyperlink', 'Caption' => 'Odkaz', 'Default' => ''),
53 ),
54 ));
55 Core::Cast($this->System)->FormManager->RegisterClass('NewsCategory', array(
56 'Title' => 'Kategorie aktualit',
57 'Table' => 'NewsCategory',
58 'Items' => array(
59 'Caption' => array('Type' => 'String', 'Caption' => 'Titulek', 'Default' => ''),
60 'RSS' => array('Type' => 'Hyperlink', 'Caption' => 'Zdroj RSS', 'Default' => ''),
61 'Permission' => array('Type' => 'Boolean', 'Caption' => 'Veřejné upravitelné', 'Default' => ''),
62 'Sequence' => array('Type' => 'Integer', 'Caption' => 'Pořadí', 'Default' => ''),
63 'Group' => array('Type' => 'Integer', 'Caption' => 'Skupina', 'Default' => ''),
64 'News' => array('Type' => 'TNewsList', 'Caption' => 'Aktuality', 'Default' => ''),
65 ),
66 ));
67 Core::Cast($this->System)->FormManager->RegisterFormType('TNewsCategory', array(
68 'Type' => 'Reference',
69 'Table' => 'NewsCategory',
70 'Id' => 'Id',
71 'Name' => 'Caption',
72 'Filter' => '1',
73 ));
74 Core::Cast($this->System)->FormManager->RegisterFormType('TNewsList', array(
75 'Type' => 'ManyToOne',
76 'Table' => 'News',
77 'Id' => 'Id',
78 'Ref' => 'Category',
79 'Filter' => '1',
80 ));
81
82 if ($this->System->ModuleManager->ModulePresent('Search'))
83 {
84 ModuleSearch::Cast($this->System->GetModule('Search'))->RegisterSearch('Novinky', 'News', array('Title', 'Content'));
85 }
86 }
87
88 function GetIntranetCondition(): string
89 {
90 if (IsInternetAddr()) return ' AND (`Intranet`=0)';
91 else return '';
92 }
93
94 function ShowNews(string $Category, int $ItemCount, int $DaysAgo): string
95 {
96 $ItemCount = abs($ItemCount);
97 $DaysAgo = abs($DaysAgo);
98 $DbResult = $this->Database->select('NewsCategory', '*', 'Id='.$Category);
99 $Row = $DbResult->fetch_array();
100 $Output = '<div class="NewsPanel"><div class="Title">'.$Row['Caption'];
101 $Output .= '<div class="Action"><a href="aktuality/?category='.$Category.'">Zobrazit</a>';
102 if (ModuleUser::Cast($this->System->GetModule('User'))->User->CheckPermission('News', 'Insert', 'Group', $Category))
103 $Output .= ' <a href="aktuality/?action=add&amp;category='.$Category.'">Přidat</a>';
104 $Output .= '</div></div><div class="Content">';
105 $DbResult = $this->Database->query('SELECT `News`.*, `User`.`Name` FROM `News` LEFT JOIN `User` ON `User`.`Id`=`News`.`User`'.
106 ' WHERE (`News`.`Category`='.$Category.') AND (DATE_SUB(NOW(), INTERVAL '.$DaysAgo.' DAY) < `News`.`Date`)'.$this->GetIntranetCondition().
107 ' ORDER BY `News`.`Date` DESC LIMIT 0,'.$ItemCount);
108
109 $Index = 0;
110 $FontSize = 12;
111 if ($DbResult->num_rows > 0)
112 {
113 $Output .= '<table class="NewsTable">';
114 while ($Row = $DbResult->fetch_array())
115 {
116 if ($Row['Name'] == '') $Author = $Row['Author'];
117 else $Author = $Row['Name'];
118 $Output .= '<tr><td onclick="window.location=\'aktuality/?action=view&amp;id='.$Row['Id'].
119 '\'" onmouseover="zobraz('."'new".$Category.$Index."'".')" style="cursor: pointer; margin: 0px;">'.
120 '<table class="NewsItemFrame"><tr><td style="font-size: '.$FontSize.'pt"><strong>'.$Row['Title'].'</strong></td>'.
121 '<td align="right" style="font-size: '.$FontSize.'pt">'.$Author.' ('.HumanDate($Row['Date']).')</td></tr></table>';
122 $Output .= '<div id="new'.$Category.$Index.'" class="NewsTableItem">'.$this->ModifyContent($Row['Content']);
123 if ($Row['Link'] != '') $Output .= '<br/><a href="'.$Row['Link'].'">Odkaz</a>';
124
125 if ($Row['Enclosure'] != '')
126 {
127 $Output .= '<br />Přílohy: ';
128 $Enclosures = explode(';', $Row['Enclosure']);
129 foreach ($Enclosures as $Enclosure)
130 {
131 if (file_exists($this->UploadedFilesFolder.$Enclosure))
132 $Output .= ' <a href="'.$this->UploadedFilesFolder.$Enclosure.'">'.$Enclosure.'</a>';
133 }
134 }
135 $Output .= '</div></td></tr>';
136 $Index = $Index + 1;
137 $FontSize = $FontSize - 1;
138 }
139 $Output .= '</table>';
140 }
141 $Output .= '</div></div>';
142 return $Output;
143 }
144
145 function LoadSettingsFromCookies(): void
146 {
147 // Initialize default news setting
148 $this->NewsSetting = array();
149 $I = 1;
150 $DbResult = $this->Database->select('NewsCategory', '*', '1 ORDER BY Sequence');
151 while ($NewsCategory = $DbResult->fetch_array())
152 {
153 $this->NewsSetting[] = array('CategoryId' => $NewsCategory['Id'], 'Index' => $I, 'Enabled' => 1,
154 'ItemCount' => Core::Cast($this->System)->Config['Web']['News']['Count'], 'DaysAgo' =>
155 Core::Cast($this->System)->Config['Web']['News']['DaysAgo'], 'Group' => $NewsCategory['Group']);
156 $I++;
157 }
158 // Merge defaults with user setting
159 if (array_key_exists('NewsSetting', $_COOKIE))
160 {
161 $NewsSettingCookie = unserialize($_COOKIE['NewsSetting']);
162 foreach ($this->NewsSetting as $Index => $this->NewsSetting)
163 {
164 if (array_key_exists($Index, $NewsSettingCookie))
165 $this->NewsSetting[$Index] = array_merge($this->NewsSetting, $NewsSettingCookie[$Index]);
166 }
167 }
168 }
169
170 function Show(): string
171 {
172 $Output = '';
173
174 $this->LoadSettingsFromCookies();
175
176 if (array_key_exists('Action', $_GET))
177 {
178 // Show news customize menu
179 if ($_GET['Action'] == 'CustomizeNews')
180 {
181 $Output .= $this->ShowCustomizeMenu();
182 }
183 }
184
185 $Output .= '<div onmouseout="skryj(predchozi)">';
186
187 $ColumnCount = 2;
188 $Output .= '<table style="width: 100%"><tr>';
189 for ($Column = 1; $Column <= $ColumnCount; $Column++)
190 {
191 $Output .= '<td style="vertical-align: top; width: '.round(100 / $ColumnCount).'%;">';
192 foreach ($this->NewsSetting as $SettingItem)
193 if (($SettingItem['Enabled'] == 1) and ($SettingItem['Group'] == $Column))
194 $Output .= $this->ShowNews($SettingItem['CategoryId'], $SettingItem['ItemCount'], $SettingItem['DaysAgo']);
195 $Output .= '</td>';
196 }
197 $Output .= '</tr></table>';
198
199 $Output .= '<a href="aktuality/subscription"><img class="RSSIcon" src="images/rss20.png" alt="Aktuality přes RSS" /></a> <a href="aktuality/subscription">Automatické sledování novinek</a>';
200 $Output .= '</div>';
201 return $Output;
202 }
203
204 function ShowCustomizeMenu(): string
205 {
206 $Output = '<form action="?Action=CustomizeNewsSave" method="post">';
207 $Output .= '<table width="100%" cellspacing="0" border="1"><tr><td><table cellspacing="0" width="100%">';
208 $Output .= '<tr><th>Kategorie</th><th>Pozice</th><th>Zobrazit</th><th>Max. počet</th><th>Posledních dnů</th><th>Sloupec</th></tr>';
209 $I = 0;
210 foreach ($this->NewsSetting as $SettingItem)
211 {
212 $DbResult = $this->Database->select('NewsCategory', '*', 'Id='.$SettingItem['CategoryId']);
213 $NewsCategory = $DbResult->fetch_array();
214 $Output .= '<tr><td>'.$NewsCategory['Caption'].'</td><td align="center"><input type="text" size="2" name="NewsCategoryIndex'.$I.'" value="'.$SettingItem['Index'].'" /></td><td align="center"><input type="checkbox" name="NewsCategoryEnabled'.$I.'"';
215 if ($SettingItem['Enabled'] == 1) $Output .= ' checked="checked"';
216 $Output .= ' /></td>'.
217 '<td align="center"><input type="text" size="2" name="NewsCategoryCount'.$I.'" value="'.$SettingItem['ItemCount'].'" />'.
218 '<input type="hidden" name="NewsCategoryId'.$I.'" value="'.$SettingItem['CategoryId'].'" /></td>'.
219 '<td align="center"><input type="text" size="3" name="NewsCategoryDaysAgo'.$I.'" value="'.$SettingItem['DaysAgo'].'" /></td>'.
220 '<td><input type="text" size="3" name="NewsColumn'.$I.'" value="'.$SettingItem['Group'].'"/></td></tr>';
221 $I++;
222 }
223 $Output .= '</table><input type="hidden" name="NewsCategoryCount" value="'.count($this->NewsSetting).'" /><input type="submit" value="Uložit" /></form></td></tr></table><br>';
224 return $Output;
225 }
226
227 function CustomizeSave(): void
228 {
229 $Checkbox = array('' => 0, 'on' => 1);
230 $Setting = array();
231 for ($I = 0; $I < $_POST['NewsCategoryCount']; $I++)
232 {
233 if (($_POST['NewsCategoryDaysAgo'.$I] * 1) < 0) $_POST['NewsCategoryIndex'.$I] = 0;
234 if (($_POST['NewsCategoryCount'.$I] * 1) < 0) $_POST['NewsCategoryCount'.$I] = 0;
235 if (($_POST['NewsColumn'.$I] * 1) < 1) $_POST['NewsColumn'.$I] = 1;
236 if (!array_key_exists('NewsCategoryEnabled'.$I, $_POST)) $_POST['NewsCategoryEnabled'.$I] = '';
237 $Setting[] = array('CategoryId' => $_POST['NewsCategoryId'.$I], 'Enabled' => $Checkbox[$_POST['NewsCategoryEnabled'.$I]], 'ItemCount' => ($_POST['NewsCategoryCount'.$I]*1), 'DaysAgo' => ($_POST['NewsCategoryDaysAgo'.$I]*1), 'Index' => ($_POST['NewsCategoryIndex'.$I]*1),
238 'Group' => $_POST['NewsColumn'.$I]);
239 }
240 // Sort indexes
241 usort($Setting, 'CategoryItemCompare');
242 $Setting = array_reverse($Setting);
243 // Normalize indexes
244 foreach ($Setting as $Index => $Item)
245 $Setting[$Index]['Index'] = $Index + 1;
246
247 // Store new cookie value
248 $_COOKIE['NewsSetting'] = serialize($Setting);
249 setcookie('NewsSetting', $_COOKIE['NewsSetting'], time() + 60 * 60 * 24 * 365);
250 }
251
252 function ModifyContentPrefix(string $Content, string $Prefix): string
253 {
254 $Result = '';
255
256 // Make HTML link from URL
257 $I = 0;
258 while (strpos($Content, $Prefix) !== false)
259 {
260 $I = strpos($Content, $Prefix);
261 if (($I > 0) and ($Content[$I - 1] != '"'))
262 {
263 $Result .= substr($Content, 0, $I);
264 $Content = substr($Content, $I);
265 if (strpos($Content, ' ') !== false)
266 $URL = substr($Content, 0, strpos($Content, ' '));
267 else $URL = substr($Content, 0);
268 $Result .= '<a href="'.$URL.'">'.$URL.'</a>';
269 $Content = substr($Content, strlen($URL));
270 } else
271 {
272 $Result .= substr($Content, 0, $I + 1);
273 $Content = substr($Content, $I + 1);
274 }
275 }
276 $Result .= $Content;
277 return $Result;
278 }
279
280 function ModifyContent(string $Content): string
281 {
282 return $this->ModifyContentPrefix($this->ModifyContentPrefix($Content, 'http://'), 'https://');
283 }
284
285 static function Cast(Module $Module): ModuleNews
286 {
287 if ($Module instanceof ModuleNews)
288 {
289 return $Module;
290 }
291 throw new Exception('Expected ModuleNews type but got '.gettype($Module));
292 }
293}
294
295class News extends Model
296{
297 static function GetModelDesc(): ModelDesc
298 {
299 $Desc = new ModelDesc(self::GetClassName());
300 $Desc->AddReference('Category', NewsCategory::GetClassName(), true);
301 $Desc->AddString('Title');
302 $Desc->AddText('Content');
303 $Desc->AddDate('Date');
304 $Desc->AddString('Author');
305 $Desc->AddString('Enclosure');
306 $Desc->AddReference('User', User::GetClassName());
307 $Desc->AddString('IP');
308 $Desc->AddString('Link');
309 return $Desc;
310 }
311}
312
313class NewsCategory extends Model
314{
315 static function GetModelDesc(): ModelDesc
316 {
317 $Desc = new ModelDesc(self::GetClassName());
318 $Desc->AddString('Caption');
319 $Desc->AddString('RSS');
320 $Desc->AddBoolean('Permission');
321 $Desc->AddInteger('Sequence');
322 $Desc->AddInteger('Group');
323 return $Desc;
324 }
325}
Note: See TracBrowser for help on using the repository browser.