| 1 | <?php | 
|---|
| 2 |  | 
|---|
| 3 | include_once(dirname(__FILE__).'/NewsPage.php'); | 
|---|
| 4 | include_once(dirname(__FILE__).'/NewsSource.php'); | 
|---|
| 5 | include_once(dirname(__FILE__).'/Import/Vismo.php'); | 
|---|
| 6 | include_once(dirname(__FILE__).'/Import/ZdechovNET.php'); | 
|---|
| 7 |  | 
|---|
| 8 | function CategoryItemCompare($Item1, $Item2) | 
|---|
| 9 | { | 
|---|
| 10 | if ($Item1['Index'] == $Item2['Index']) return 0; | 
|---|
| 11 | return $Item1['Index'] > $Item2['Index'] ? -1 : 1; | 
|---|
| 12 | } | 
|---|
| 13 |  | 
|---|
| 14 | class 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&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&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 |  | 
|---|
| 295 | class 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 |  | 
|---|
| 313 | class 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 | } | 
|---|