action.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. <?php
  2. /*
  3. @nom: action
  4. @auteur: Idleman (idleman@idleman.fr)
  5. @description: Page de gestion des évenements non liés a une vue particulière (appels ajax, requetes sans resultats etc...)
  6. */
  7. if(!ini_get('safe_mode')) @set_time_limit(0);
  8. require_once("common.php");
  9. ///@TODO: déplacer dans common.php?
  10. $commandLine = 'cli'==php_sapi_name();
  11. if ($commandLine) {
  12. $action = 'commandLine';
  13. } else {
  14. $action = @$_['action'];
  15. }
  16. ///@TODO: pourquoi ne pas refuser l'accès dès le début ?
  17. Plugin::callHook("action_pre_case", array(&$_,$myUser));
  18. //Execution du code en fonction de l'action
  19. switch ($action){
  20. case 'commandLine':
  21. case 'synchronize':
  22. require_once("SimplePie.class.php");
  23. $syncCode = $configurationManager->get('synchronisationCode');
  24. $syncGradCount = $configurationManager->get('syncGradCount');
  25. if ( false==$myUser
  26. && !$commandLine
  27. && !(isset($_['code'])
  28. && $configurationManager->get('synchronisationCode')!=null
  29. && $_['code']==$configurationManager->get('synchronisationCode')
  30. )
  31. ) {
  32. die(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  33. }
  34. Functions::triggerDirectOutput();
  35. if (!$commandLine){
  36. echo '<html>
  37. <head>
  38. <link rel="stylesheet" href="./templates/'.$theme.'/css/style.css">
  39. </head>
  40. <body>
  41. <div class="sync">';
  42. }
  43. $synchronisationType = $configurationManager->get('synchronisationType');
  44. $synchronisationCustom = array();
  45. Plugin::callHook("action_before_synchronisationtype", array(&$synchronisationCustom,&$synchronisationType,&$commandLine,$configurationManager,$start));
  46. if(isset($synchronisationCustom['type'])){
  47. $feeds = $synchronisationCustom['feeds'];
  48. $syncTypeStr = _t('SYNCHRONISATION_TYPE').' : '._t($synchronisationCustom['type']);
  49. }elseif('graduate'==$synchronisationType){
  50. // sélectionne les 10 plus vieux flux
  51. $feeds = $feedManager->loadAll(null,'lastupdate', $syncGradCount);
  52. $syncTypeStr = _t('SYNCHRONISATION_TYPE').' : '._t('GRADUATE_SYNCHRONISATION');
  53. }else{
  54. // sélectionne tous les flux, triés par le nom
  55. $feeds = $feedManager->populate('name');
  56. $syncTypeStr = _t('SYNCHRONISATION_TYPE').' : '._t('FULL_SYNCHRONISATION');
  57. }
  58. if(!isset($synchronisationCustom['no_normal_synchronize'])){
  59. $feedManager->synchronize($feeds, $syncTypeStr, $commandLine, $configurationManager, $start);
  60. }
  61. break;
  62. case 'readAll':
  63. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  64. $whereClause = array();
  65. $whereClause['unread'] = '1';
  66. if(isset($_['feed']))$whereClause['feed'] = $_['feed'];
  67. if(isset($_['last-event-id']))$whereClause['id'] = '<= ' . $_['last-event-id'];
  68. $eventManager->change(array('unread'=>'0'),$whereClause);
  69. if(!Functions::isAjaxCall()){
  70. header('location: ./index.php');
  71. }
  72. break;
  73. case 'readFolder':
  74. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  75. $feeds = $feedManager->loadAllOnlyColumn('id',array('folder'=>$_['folder']));
  76. foreach($feeds as $feed){
  77. $whereClause['feed'] = $feed->getId();
  78. if(isset($_['last-event-id']))$whereClause['id'] = '<= ' . $_['last-event-id'];
  79. $eventManager->change(array('unread'=>'0'),$whereClause);
  80. }
  81. if (!Functions::isAjaxCall()){
  82. header('location: ./index.php');
  83. }
  84. break;
  85. case 'updateConfiguration':
  86. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  87. //Ajout des préférences et réglages
  88. $configurationManager->put('root',(substr($_['root'], strlen($_['root'])-1)=='/'?$_['root']:$_['root'].'/'));
  89. $configurationManager->put('articleDisplayAnonymous',$_['articleDisplayAnonymous']);
  90. $configurationManager->put('articlePerPages',$_['articlePerPages']);
  91. $configurationManager->put('articleDisplayLink',$_['articleDisplayLink']);
  92. $configurationManager->put('articleDisplayDate',$_['articleDisplayDate']);
  93. $configurationManager->put('articleDisplayAuthor',$_['articleDisplayAuthor']);
  94. $configurationManager->put('articleDisplayHomeSort',$_['articleDisplayHomeSort']);
  95. $configurationManager->put('articleDisplayFolderSort',$_['articleDisplayFolderSort']);
  96. $configurationManager->put('articleDisplayMode',$_['articleDisplayMode']);
  97. $configurationManager->put('synchronisationType',$_['synchronisationType']);
  98. $configurationManager->put('synchronisationEnableCache',$_['synchronisationEnableCache']);
  99. $configurationManager->put('synchronisationForceFeed',$_['synchronisationForceFeed']);
  100. $configurationManager->put('feedMaxEvents',$_['feedMaxEvents']);
  101. $configurationManager->put('language',$_['ChgLanguage']);
  102. $configurationManager->put('theme',$_['ChgTheme']);
  103. $userManager->change(array('login'=>$_['login']),array('id'=>$myUser->getId()));
  104. if(trim($_['password'])!='') {
  105. $salt = User::generateSalt();
  106. $userManager->change(array('password'=>User::encrypt($_['password'], $salt)),array('id'=>$myUser->getId()));
  107. /* /!\ En multi-utilisateur, il faudra changer l'information au
  108. niveau du compte lui-même et non au niveau du déploiement comme
  109. ici. C'est ainsi parce que c'est plus efficace de stocker le sel
  110. dans la config que dans le fichier de constantes, difficile à
  111. modifier. */
  112. $oldSalt = $configurationManager->get('cryptographicSalt');
  113. if (empty($oldSalt))
  114. /* Pendant la migration à ce système, les déploiements
  115. ne posséderont pas cette donnée. */
  116. $configurationManager->add('cryptographicSalt', $salt);
  117. else
  118. $configurationManager->change(array('value'=>$salt), array('key'=>'cryptographicSalt'));
  119. }
  120. header('location: ./settings.php#preferenceBloc');
  121. break;
  122. case 'purge':
  123. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  124. $eventManager->truncate();
  125. header('location: ./settings.php');
  126. break;
  127. case 'exportFeed':
  128. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  129. /*********************/
  130. /** Export **/
  131. /*********************/
  132. if(isset($_POST['exportButton'])){
  133. $opml = new Opml();
  134. $xmlStream = $opml->export();
  135. header('Content-Description: File Transfer');
  136. header('Content-Type: application/octet-stream');
  137. header('Content-Disposition: attachment; filename=leed-'.date('d-m-Y').'.opml');
  138. header('Content-Transfer-Encoding: binary');
  139. header('Expires: 0');
  140. header('Cache-Control: must-revalidate');
  141. header('Pragma: public');
  142. header('Content-Length: ' . strlen($xmlStream));
  143. /*
  144. //A decommenter dans le cas ou on a des pb avec ie
  145. if(preg_match('/msie|(microsoft internet explorer)/i', $_SERVER['HTTP_USER_AGENT'])){
  146. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  147. header('Pragma: public');
  148. }else{
  149. header('Pragma: no-cache');
  150. }
  151. */
  152. ob_clean();
  153. flush();
  154. echo $xmlStream;
  155. }
  156. break;
  157. case 'importForm':
  158. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  159. echo '<html style="height:auto;"><link rel="stylesheet" href="templates/'.$theme.'/css/style.css">
  160. <body style="height:auto;">
  161. <form action="action.php?action=importFeed" method="POST" enctype="multipart/form-data">
  162. <p>'._t('OPML_FILE').' : <input name="newImport" type="file"/> <button name="importButton">'._t('IMPORT').'</button></p>
  163. <p>'._t('IMPORT_COFFEE_TIME').'</p>
  164. </form>
  165. </body>
  166. </html>
  167. ';
  168. break;
  169. case 'synchronizeForm':
  170. if(isset($myUser) && $myUser!=false){
  171. echo '<link rel="stylesheet" href="templates/'.$theme.'/css/style.css">
  172. <a class="button" href="action.php?action=synchronize">'._t('SYNCHRONIZE_NOW').'</a>
  173. <p>'._t('SYNCHRONIZE_COFFEE_TIME').'</p>
  174. ';
  175. }else{
  176. echo _t('YOU_MUST_BE_CONNECTED_ACTION');
  177. }
  178. break;
  179. case 'changeFolderState':
  180. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  181. $folderManager->change(array('isopen'=>$_['isopen']),array('id'=>$_['id']));
  182. break;
  183. case 'importFeed':
  184. // On ne devrait pas mettre de style ici.
  185. echo "<html>
  186. <style>
  187. a {
  188. color:#F16529;
  189. }
  190. html,body{
  191. font-family:Verdana;
  192. font-size: 11px;
  193. }
  194. .error{
  195. background-color:#C94141;
  196. color:#ffffff;
  197. padding:5px;
  198. border-radius:5px;
  199. margin:10px 0px 10px 0px;
  200. box-shadow: 0 0 3px 0 #810000;
  201. }
  202. .error a{
  203. color:#ffffff;
  204. }
  205. </style>
  206. </style><body>
  207. \n";
  208. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  209. if(!isset($_POST['importButton'])) break;
  210. $opml = new Opml();
  211. echo "<h3>"._t('IMPORT')."</h3><p>"._t('PENDING')."</p>\n";
  212. try {
  213. $errorOutput = $opml->import($_FILES['newImport']['tmp_name']);
  214. } catch (Exception $e) {
  215. $errorOutput = array($e->getMessage());
  216. }
  217. if (empty($errorOutput)) {
  218. echo "<p>"._t('IMPORT_NO_PROBLEM')."</p>\n";
  219. } else {
  220. echo "<div class='error'>"._t('IMPORT_ERROR')."\n";
  221. foreach($errorOutput as $line) {
  222. echo "<p>$line</p>\n";
  223. }
  224. echo "</div>";
  225. }
  226. if (!empty($opml->alreadyKnowns)) {
  227. echo "<h3>"._t('IMPORT_FEED_ALREADY_KNOWN')." : </h3>\n<ul>\n";
  228. foreach($opml->alreadyKnowns as $alreadyKnown) {
  229. foreach($alreadyKnown as &$elt) $elt = htmlspecialchars($elt);
  230. $text = Functions::truncate($alreadyKnown->feedName, 60);
  231. echo "<li><a target='_parent' href='{$alreadyKnown->xmlUrl}'>"
  232. ."{$text}</a></li>\n";
  233. }
  234. echo "</ul>\n";
  235. }
  236. $syncLink = "action.php?action=synchronize&format=html";
  237. echo "<p>";
  238. echo "<a href='$syncLink' style='text-decoration:none;font-size:3em'>"
  239. ."↺</a>";
  240. echo "<a href='$syncLink'>"._t('CLIC_HERE_SYNC_IMPORT')."</a>";
  241. echo "<p></body></html>\n";
  242. break;
  243. case 'addFeed':
  244. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  245. require_once("SimplePie.class.php");
  246. if(!isset($_['newUrl'])) break;
  247. $newFeed = new Feed();
  248. $newFeed->setUrl(Functions::clean_url($_['newUrl']));
  249. if ($newFeed->notRegistered()) {
  250. ///@TODO: avertir l'utilisateur du doublon non ajouté
  251. $newFeed->getInfos();
  252. $newFeed->setFolder(
  253. (isset($_['newUrlCategory'])?$_['newUrlCategory']:1)
  254. );
  255. $newFeed->save();
  256. $enableCache = ($configurationManager->get('synchronisationEnableCache')=='')?0:$configurationManager->get('synchronisationEnableCache');
  257. $forceFeed = ($configurationManager->get('synchronisationForceFeed')=='')?0:$configurationManager->get('synchronisationForceFeed');
  258. $newFeed->parse(time(), $_, $enableCache, $forceFeed);
  259. Plugin::callHook("action_after_addFeed", array(&$newFeed));
  260. }
  261. header('location: ./settings.php#manageBloc');
  262. break;
  263. case 'changeFeedFolder':
  264. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  265. if(isset($_['feed'])){
  266. $feedManager->change(array('folder'=>$_['folder']),array('id'=>$_['feed']));
  267. }
  268. header('location: ./settings.php');
  269. break;
  270. case 'removeFeed':
  271. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  272. if(isset($_GET['id'])){
  273. $feedManager->delete(array('id'=>$_['id']));
  274. $eventManager->delete(array('feed'=>$_['id']));
  275. Plugin::callHook("action_after_removeFeed", array($_['id']));
  276. }
  277. header('location: ./settings.php');
  278. break;
  279. case 'addFolder':
  280. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  281. if(isset($_['newFolder'])){
  282. $folder = new Folder();
  283. if($folder->rowCount(array('name'=>$_['newFolder']))==0){
  284. $folder->setParent(-1);
  285. $folder->setIsopen(0);
  286. $folder->setName($_['newFolder']);
  287. $folder->save();
  288. }
  289. }
  290. header('location: ./settings.php');
  291. break;
  292. case 'renameFolder':
  293. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  294. if(isset($_['id'])){
  295. $folderManager->change(array('name'=>$_['name']),array('id'=>$_['id']));
  296. }
  297. break;
  298. case 'renameFeed':
  299. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  300. if(isset($_['id'])){
  301. $feedManager->change(array('name'=>$_['name'],'url'=>Functions::clean_url($_['url'])),array('id'=>$_['id']));
  302. }
  303. break;
  304. case 'removeFolder':
  305. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  306. if(isset($_['id']) && is_numeric($_['id']) && $_['id']>0){
  307. $eventManager->customExecute('DELETE FROM `'.MYSQL_PREFIX.'event` WHERE `'.MYSQL_PREFIX.'event`.`feed` in (SELECT `'.MYSQL_PREFIX.'feed`.`id` FROM `'.MYSQL_PREFIX.'feed` WHERE `'.MYSQL_PREFIX.'feed`.`folder` =\''.intval($_['id']).'\') ;');
  308. $feedManager->delete(array('folder'=>$_['id']));
  309. $folderManager->delete(array('id'=>$_['id']));
  310. }
  311. header('location: ./settings.php');
  312. break;
  313. case 'readContent':
  314. if($myUser==false) {
  315. $response_array['status'] = 'noconnect';
  316. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  317. header('Content-type: application/json');
  318. echo json_encode($response_array);
  319. exit();
  320. }
  321. if(isset($_['id'])){
  322. $event = $eventManager->load(array('id'=>$_['id']));
  323. $eventManager->change(array('unread'=>'0'),array('id'=>$_['id']));
  324. }
  325. break;
  326. case 'unreadContent':
  327. if($myUser==false) {
  328. $response_array['status'] = 'noconnect';
  329. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  330. header('Content-type: application/json');
  331. echo json_encode($response_array);
  332. exit();
  333. }
  334. if(isset($_['id'])){
  335. $event = $eventManager->load(array('id'=>$_['id']));
  336. $eventManager->change(array('unread'=>'1'),array('id'=>$_['id']));
  337. }
  338. break;
  339. case 'addFavorite':
  340. if($myUser==false) {
  341. $response_array['status'] = 'noconnect';
  342. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  343. header('Content-type: application/json');
  344. echo json_encode($response_array);
  345. exit();
  346. }
  347. $eventManager->change(array('favorite'=>'1'),array('id'=>$_['id']));
  348. break;
  349. case 'removeFavorite':
  350. if($myUser==false) {
  351. $response_array['status'] = 'noconnect';
  352. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  353. header('Content-type: application/json');
  354. echo json_encode($response_array);
  355. exit();
  356. }
  357. $eventManager->change(array('favorite'=>'0'),array('id'=>$_['id']));
  358. break;
  359. case 'login':
  360. define('RESET_PASSWORD_FILE', 'resetPassword');
  361. if (file_exists(RESET_PASSWORD_FILE)) {
  362. /* Pour réinitialiser le mot de passe :
  363. * créer le fichier RESET_PASSWORD_FILE vide.
  364. * Le nouveau mot de passe sera celui fourni à la connexion.
  365. */
  366. @unlink(RESET_PASSWORD_FILE);
  367. if (file_exists(RESET_PASSWORD_FILE)) {
  368. $message = 'Unable to remove "'.RESET_PASSWORD_FILE.'"!';
  369. /* Pas supprimable ==> on ne remet pas à zéro */
  370. } else {
  371. $resetPassword = $_['password'];
  372. assert('!empty($resetPassword)');
  373. $tmpUser = User::get($_['login']);
  374. if (false===$tmpUser) {
  375. $message = "Unknown user '{$_['login']}'! No password reset.";
  376. } else {
  377. $id = $tmpUser->getId();
  378. $salt = $configurationManager->get('cryptographicSalt');
  379. $userManager->change(
  380. array('password'=>User::encrypt($resetPassword, $salt)),
  381. array('id'=>$id)
  382. );
  383. $message = "User '{$_['login']}' (id=$id) Password reset to '$resetPassword'.";
  384. }
  385. }
  386. error_log($message);
  387. }
  388. if(isset($_['usr'])){
  389. $user = User::existAuthToken($_['usr']);
  390. if($user==false){
  391. exit("error"); //@TODO: traduire
  392. }else{
  393. $_SESSION['currentUser'] = serialize($user);
  394. header('location: ./action.php?action=addFeed&newUrl='.$_['newUrl']);
  395. exit();
  396. }
  397. }else{
  398. $salt = $configurationManager->get('cryptographicSalt');
  399. if (empty($salt)) $salt = '';
  400. $user = $userManager->exist($_['login'],$_['password'],$salt);
  401. if($user==false){
  402. header('location: ./index.php?action=wrongLogin');
  403. }else{
  404. $_SESSION['currentUser'] = serialize($user);
  405. if (isset($_['rememberMe'])) $user->setStayConnected();
  406. header('location: ./index.php');
  407. }
  408. exit();
  409. }
  410. break;
  411. case 'changePluginState':
  412. if($myUser==false) exit(_t('YOU_MUST_BE_CONNECTED_ACTION'));
  413. if($_['state']=='0'){
  414. Plugin::enabled($_['plugin']);
  415. }else{
  416. Plugin::disabled($_['plugin']);
  417. }
  418. header('location: ./settings.php#pluginBloc');
  419. break;
  420. case 'logout':
  421. User::delStayConnected();
  422. $_SESSION = array();
  423. session_unset();
  424. session_destroy();
  425. header('location: ./index.php');
  426. break;
  427. case 'displayOnlyUnreadFeedFolder':
  428. if($myUser==false) {
  429. $response_array['status'] = 'noconnect';
  430. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  431. header('Content-type: application/json');
  432. echo json_encode($response_array);
  433. exit();
  434. }
  435. $configurationManager->put('displayOnlyUnreadFeedFolder',$_['displayOnlyUnreadFeedFolder']);
  436. break;
  437. case 'displayFeedIsVerbose':
  438. if($myUser==false) {
  439. $response_array['status'] = 'noconnect';
  440. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  441. header('Content-type: application/json');
  442. echo json_encode($response_array);
  443. exit();
  444. }
  445. // changement du statut isverbose du feed
  446. $feed = new Feed();
  447. $feed = $feed->getById($_['idFeed']);
  448. $feed->setIsverbose(($_['displayFeedIsVerbose']=="0"?1:0));
  449. $feed->save();
  450. break;
  451. case 'optionFeedIsVerbose':
  452. if($myUser==false) {
  453. $response_array['status'] = 'noconnect';
  454. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  455. header('Content-type: application/json');
  456. echo json_encode($response_array);
  457. exit();
  458. }
  459. // changement du statut de l'option
  460. $configurationManager = new Configuration();
  461. $conf = $configurationManager->getAll();
  462. $configurationManager->put('optionFeedIsVerbose',($_['optionFeedIsVerbose']=="0"?0:1));
  463. break;
  464. case 'articleDisplayMode':
  465. if($myUser==false) {
  466. $response_array['status'] = 'noconnect';
  467. $response_array['texte'] = _t('YOU_MUST_BE_CONNECTED_ACTION');
  468. header('Content-type: application/json');
  469. echo json_encode($response_array);
  470. exit();
  471. }
  472. // chargement du content de l'article souhaité
  473. $newEvent = new Event();
  474. $event = $newEvent->getById($_['event_id']);
  475. if ($_['articleDisplayMode']=='content'){
  476. //error_log(print_r($_SESSION['events'],true));
  477. $content = $event->getContent();
  478. } else {
  479. $content = $event->getDescription();
  480. }
  481. echo $content;
  482. break;
  483. default:
  484. require_once("SimplePie.class.php");
  485. Plugin::callHook("action_post_case", array(&$_,$myUser));
  486. //exit('0');
  487. break;
  488. //Installation d'un nouveau plugin
  489. case 'installPlugin':
  490. Plugin::install($_['zip']);
  491. break;
  492. }
  493. ?>