From b4463cb69e64ff2dfe840ef69f1b825ecdbee43e Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 15 Dec 2013 11:28:27 +0100 Subject: Problème casse renommage répertoire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/configureController.php | 415 ++++++++++++++++++++++++++++++ app/Controllers/entryController.php | 112 ++++++++ app/Controllers/errorController.php | 26 ++ app/Controllers/feedController.php | 428 +++++++++++++++++++++++++++++++ app/Controllers/indexController.php | 277 ++++++++++++++++++++ app/Controllers/javascriptController.php | 13 + 6 files changed, 1271 insertions(+) create mode 100755 app/Controllers/configureController.php create mode 100755 app/Controllers/entryController.php create mode 100644 app/Controllers/errorController.php create mode 100755 app/Controllers/feedController.php create mode 100755 app/Controllers/indexController.php create mode 100755 app/Controllers/javascriptController.php (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php new file mode 100755 index 000000000..62fead315 --- /dev/null +++ b/app/Controllers/configureController.php @@ -0,0 +1,415 @@ +view->conf) && !is_logged ()) { + Minz_Error::error ( + 403, + array ('error' => array (Minz_Translate::t ('access_denied'))) + ); + } + + $catDAO = new FreshRSS_CategoryDAO (); + $catDAO->checkDefault (); + } + + public function categorizeAction () { + $feedDAO = new FreshRSS_FeedDAO (); + $catDAO = new FreshRSS_CategoryDAO (); + $catDAO->checkDefault (); + $defaultCategory = $catDAO->getDefault (); + $defaultId = $defaultCategory->id (); + + if (Minz_Request::isPost ()) { + $cats = Minz_Request::param ('categories', array ()); + $ids = Minz_Request::param ('ids', array ()); + $newCat = trim (Minz_Request::param ('new_category', '')); + + foreach ($cats as $key => $name) { + if (strlen ($name) > 0) { + $cat = new FreshRSS_Category ($name); + $values = array ( + 'name' => $cat->name (), + 'color' => $cat->color () + ); + $catDAO->updateCategory ($ids[$key], $values); + } elseif ($ids[$key] != $defaultId) { + $feedDAO->changeCategory ($ids[$key], $defaultId); + $catDAO->deleteCategory ($ids[$key]); + } + } + + if ($newCat != '') { + $cat = new FreshRSS_Category ($newCat); + $values = array ( + 'id' => $cat->id (), + 'name' => $cat->name (), + 'color' => $cat->color () + ); + + if ($catDAO->searchByName ($newCat) == false) { + $catDAO->addCategory ($values); + } + } + + // notif + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('categories_updated') + ); + Minz_Session::_param ('notification', $notif); + + Minz_Request::forward (array ('c' => 'configure', 'a' => 'categorize'), true); + } + + $this->view->categories = $catDAO->listCategories (false); + $this->view->defaultCategory = $catDAO->getDefault (); + $this->view->feeds = $feedDAO->listFeeds (); + $this->view->flux = false; + + Minz_View::prependTitle (Minz_Translate::t ('categories_management') . ' - '); + } + + public function feedAction () { + $catDAO = new FreshRSS_CategoryDAO (); + $this->view->categories = $catDAO->listCategories (false); + + $feedDAO = new FreshRSS_FeedDAO (); + $this->view->feeds = $feedDAO->listFeeds (); + + $id = Minz_Request::param ('id'); + if ($id == false && !empty ($this->view->feeds)) { + $id = current ($this->view->feeds)->id (); + } + + $this->view->flux = false; + if ($id != false) { + $this->view->flux = $this->view->feeds[$id]; + + if (!$this->view->flux) { + Minz_Error::error ( + 404, + array ('error' => array (Minz_Translate::t ('page_not_found'))) + ); + } else { + if (Minz_Request::isPost () && $this->view->flux) { + $name = Minz_Request::param ('name', ''); + $description = Minz_Request::param('description', ''); + $website = Minz_Request::param('website', ''); + $url = Minz_Request::param('url', ''); + $hist = Minz_Request::param ('keep_history', 'no'); + $cat = Minz_Request::param ('category', 0); + $path = Minz_Request::param ('path_entries', ''); + $priority = Minz_Request::param ('priority', 0); + $user = Minz_Request::param ('http_user', ''); + $pass = Minz_Request::param ('http_pass', ''); + + $keep_history = false; + if ($hist == 'yes') { + $keep_history = true; + } + + $httpAuth = ''; + if ($user != '' || $pass != '') { + $httpAuth = $user . ':' . $pass; + } + + $values = array ( + 'name' => $name, + 'description' => $description, + 'website' => $website, + 'url' => $url, + 'category' => $cat, + 'pathEntries' => $path, + 'priority' => $priority, + 'httpAuth' => $httpAuth, + 'keep_history' => $keep_history + ); + + if ($feedDAO->updateFeed ($id, $values)) { + $this->view->flux->_category ($cat); + + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('feed_updated') + ); + } else { + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('error_occurred_update') + ); + } + + Minz_Session::_param ('notification', $notif); + Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => array ('id' => $id)), true); + } + + Minz_View::prependTitle (Minz_Translate::t ('rss_feed_management') . ' - ' . $this->view->flux->name () . ' - '); + } + } else { + Minz_View::prependTitle (Minz_Translate::t ('rss_feed_management') . ' - '); + } + } + + public function displayAction () { + if (Minz_Request::isPost ()) { + $current_token = $this->view->conf->token (); + + $language = Minz_Request::param ('language', 'en'); + $nb = Minz_Request::param ('posts_per_page', 10); + $mode = Minz_Request::param ('view_mode', 'normal'); + $view = Minz_Request::param ('default_view', 'a'); + $auto_load_more = Minz_Request::param ('auto_load_more', 'no'); + $display = Minz_Request::param ('display_posts', 'no'); + $onread_jump_next = Minz_Request::param ('onread_jump_next', 'no'); + $lazyload = Minz_Request::param ('lazyload', 'no'); + $sort = Minz_Request::param ('sort_order', 'DESC'); + $old = Minz_Request::param ('old_entries', 3); + $mail = Minz_Request::param ('mail_login', false); + $anon = Minz_Request::param ('anon_access', 'no'); + $token = Minz_Request::param ('token', $current_token); + $openArticle = Minz_Request::param ('mark_open_article', 'no'); + $openSite = Minz_Request::param ('mark_open_site', 'no'); + $scroll = Minz_Request::param ('mark_scroll', 'no'); + $reception = Minz_Request::param ('mark_upon_reception', 'no'); + $theme = Minz_Request::param ('theme', 'default'); + $topline_read = Minz_Request::param ('topline_read', 'no'); + $topline_favorite = Minz_Request::param ('topline_favorite', 'no'); + $topline_date = Minz_Request::param ('topline_date', 'no'); + $topline_link = Minz_Request::param ('topline_link', 'no'); + $bottomline_read = Minz_Request::param ('bottomline_read', 'no'); + $bottomline_favorite = Minz_Request::param ('bottomline_favorite', 'no'); + $bottomline_sharing = Minz_Request::param ('bottomline_sharing', 'no'); + $bottomline_tags = Minz_Request::param ('bottomline_tags', 'no'); + $bottomline_date = Minz_Request::param ('bottomline_date', 'no'); + $bottomline_link = Minz_Request::param ('bottomline_link', 'no'); + + $this->view->conf->_language ($language); + $this->view->conf->_postsPerPage (intval ($nb)); + $this->view->conf->_viewMode ($mode); + $this->view->conf->_defaultView ($view); + $this->view->conf->_autoLoadMore ($auto_load_more); + $this->view->conf->_displayPosts ($display); + $this->view->conf->_onread_jump_next ($onread_jump_next); + $this->view->conf->_lazyload ($lazyload); + $this->view->conf->_sortOrder ($sort); + $this->view->conf->_oldEntries ($old); + $this->view->conf->_mailLogin ($mail); + $this->view->conf->_anonAccess ($anon); + $this->view->conf->_token ($token); + $this->view->conf->_markWhen (array ( + 'article' => $openArticle, + 'site' => $openSite, + 'scroll' => $scroll, + 'reception' => $reception, + )); + $this->view->conf->_theme ($theme); + $this->view->conf->_topline_read ($topline_read); + $this->view->conf->_topline_favorite ($topline_favorite); + $this->view->conf->_topline_date ($topline_date); + $this->view->conf->_topline_link ($topline_link); + $this->view->conf->_bottomline_read ($bottomline_read); + $this->view->conf->_bottomline_favorite ($bottomline_favorite); + $this->view->conf->_bottomline_sharing ($bottomline_sharing); + $this->view->conf->_bottomline_tags ($bottomline_tags); + $this->view->conf->_bottomline_date ($bottomline_date); + $this->view->conf->_bottomline_link ($bottomline_link); + + $values = array ( + 'language' => $this->view->conf->language (), + 'posts_per_page' => $this->view->conf->postsPerPage (), + 'view_mode' => $this->view->conf->viewMode (), + 'default_view' => $this->view->conf->defaultView (), + 'auto_load_more' => $this->view->conf->autoLoadMore (), + 'display_posts' => $this->view->conf->displayPosts (), + 'onread_jump_next' => $this->view->conf->onread_jump_next (), + 'lazyload' => $this->view->conf->lazyload (), + 'sort_order' => $this->view->conf->sortOrder (), + 'old_entries' => $this->view->conf->oldEntries (), + 'mail_login' => $this->view->conf->mailLogin (), + 'anon_access' => $this->view->conf->anonAccess (), + 'token' => $this->view->conf->token (), + 'mark_when' => $this->view->conf->markWhen (), + 'theme' => $this->view->conf->theme (), + 'topline_read' => $this->view->conf->toplineRead () ? 'yes' : 'no', + 'topline_favorite' => $this->view->conf->toplineFavorite () ? 'yes' : 'no', + 'topline_date' => $this->view->conf->toplineDate () ? 'yes' : 'no', + 'topline_link' => $this->view->conf->toplineLink () ? 'yes' : 'no', + 'bottomline_read' => $this->view->conf->bottomlineRead () ? 'yes' : 'no', + 'bottomline_favorite' => $this->view->conf->bottomlineFavorite () ? 'yes' : 'no', + 'bottomline_sharing' => $this->view->conf->bottomlineSharing () ? 'yes' : 'no', + 'bottomline_tags' => $this->view->conf->bottomlineTags () ? 'yes' : 'no', + 'bottomline_date' => $this->view->conf->bottomlineDate () ? 'yes' : 'no', + 'bottomline_link' => $this->view->conf->bottomlineLink () ? 'yes' : 'no', + ); + + $confDAO = new FreshRSS_ConfigurationDAO (); + $confDAO->update ($values); + Minz_Session::_param ('conf', $this->view->conf); + Minz_Session::_param ('mail', $this->view->conf->mailLogin ()); + + Minz_Session::_param ('language', $this->view->conf->language ()); + Minz_Translate::reset (); + + // notif + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('configuration_updated') + ); + Minz_Session::_param ('notification', $notif); + + Minz_Request::forward (array ('c' => 'configure', 'a' => 'display'), true); + } + + $this->view->themes = FreshRSS_Themes::get(); + + Minz_View::prependTitle (Minz_Translate::t ('general_and_reading_management') . ' - '); + + $entryDAO = new FreshRSS_EntryDAO (); + $this->view->nb_total = $entryDAO->count (); + $this->view->size_total = $entryDAO->size (); + } + + public function sharingAction () { + if (Minz_Request::isPost ()) { + $this->view->conf->_sharing (array ( + 'shaarli' => Minz_Request::param ('shaarli', ''), + 'poche' => Minz_Request::param ('poche', ''), + 'diaspora' => Minz_Request::param ('diaspora', ''), + 'twitter' => Minz_Request::param ('twitter', 'no') === 'yes', + 'g+' => Minz_Request::param ('g+', 'no') === 'yes', + 'facebook' => Minz_Request::param ('facebook', 'no') === 'yes', + 'email' => Minz_Request::param ('email', 'no') === 'yes', + 'print' => Minz_Request::param ('print', 'no') === 'yes' + )); + + $confDAO = new FreshRSS_ConfigurationDAO (); + $confDAO->update ($this->view->conf->sharing ()); + Minz_Session::_param ('conf', $this->view->conf); + + // notif + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('configuration_updated') + ); + Minz_Session::_param ('notification', $notif); + + Minz_Request::forward (array ('c' => 'configure', 'a' => 'sharing'), true); + } + + Minz_View::prependTitle (Minz_Translate::t ('sharing_management') . ' - '); + + $entryDAO = new FreshRSS_EntryDAO (); + $this->view->nb_total = $entryDAO->count (); + } + + public function importExportAction () { + $catDAO = new FreshRSS_CategoryDAO (); + $this->view->categories = $catDAO->listCategories (); + + $this->view->req = Minz_Request::param ('q'); + + if ($this->view->req == 'export') { + Minz_View::_title ('freshrss_feeds.opml'); + + $this->view->_useLayout (false); + header('Content-Type: application/xml; charset=utf-8'); + header('Content-disposition: attachment; filename=freshrss_feeds.opml'); + + $feedDAO = new FreshRSS_FeedDAO (); + $catDAO = new FreshRSS_CategoryDAO (); + + $list = array (); + foreach ($catDAO->listCategories () as $key => $cat) { + $list[$key]['name'] = $cat->name (); + $list[$key]['feeds'] = $feedDAO->listByCategory ($cat->id ()); + } + + $this->view->categories = $list; + } elseif ($this->view->req == 'import' && Minz_Request::isPost ()) { + if ($_FILES['file']['error'] == 0) { + // on parse le fichier OPML pour récupérer les catégories et les flux associés + try { + list ($categories, $feeds) = opml_import ( + file_get_contents ($_FILES['file']['tmp_name']) + ); + + // On redirige vers le controller feed qui va se charger d'insérer les flux en BDD + // les flux sont mis au préalable dans des variables de Request + Minz_Request::_param ('q', 'null'); + Minz_Request::_param ('categories', $categories); + Minz_Request::_param ('feeds', $feeds); + Minz_Request::forward (array ('c' => 'feed', 'a' => 'massiveImport')); + } catch (FreshRSS_Opml_Exception $e) { + Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); + + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('bad_opml_file') + ); + Minz_Session::_param ('notification', $notif); + + Minz_Request::forward (array ( + 'c' => 'configure', + 'a' => 'importExport' + ), true); + } + } + } + + $feedDAO = new FreshRSS_FeedDAO (); + $this->view->feeds = $feedDAO->listFeeds (); + + // au niveau de la vue, permet de ne pas voir un flux sélectionné dans la liste + $this->view->flux = false; + + Minz_View::prependTitle (Translate::t ('import_export_opml') . ' - '); + } + + public function shortcutAction () { + $list_keys = array ('a', 'b', 'backspace', 'c', 'd', 'delete', 'down', 'e', 'end', 'enter', + 'escape', 'f', 'g', 'h', 'i', 'insert', 'j', 'k', 'l', 'left', + 'm', 'n', 'o', 'p', 'page_down', 'page_up', 'q', 'r', 'return', 'right', + 's', 'space', 't', 'tab', 'u', 'up', 'v', 'w', 'x', 'y', + 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', + 'f10', 'f11', 'f12'); + $this->view->list_keys = $list_keys; + $list_names = array ('mark_read', 'mark_favorite', 'go_website', 'next_entry', + 'prev_entry', 'next_page', 'prev_page', 'collapse_entry', + 'load_more'); + + if (Minz_Request::isPost ()) { + $shortcuts = Minz_Request::param ('shortcuts'); + $shortcuts_ok = array (); + + foreach ($shortcuts as $key => $value) { + if (in_array ($key, $list_names) + && in_array ($value, $list_keys)) { + $shortcuts_ok[$key] = $value; + } + } + + $this->view->conf->_shortcuts ($shortcuts_ok); + + $values = array ( + 'shortcuts' => $this->view->conf->shortcuts () + ); + + $confDAO = new FreshRSS_ConfigurationDAO (); + $confDAO->update ($values); + Minz_Session::_param ('conf', $this->view->conf); + + // notif + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('shortcuts_updated') + ); + Minz_Session::_param ('notification', $notif); + + Minz_Request::forward (array ('c' => 'configure', 'a' => 'shortcut'), true); + } + + Minz_View::prependTitle (Minz_Translate::t ('shortcuts_management') . ' - '); + } +} diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php new file mode 100755 index 000000000..a332ca8a9 --- /dev/null +++ b/app/Controllers/entryController.php @@ -0,0 +1,112 @@ +view->conf) && !is_logged ()) { + Minz_Error::error ( + 403, + array ('error' => array (Minz_Translate::t ('access_denied'))) + ); + } + + $this->params = array (); + $this->redirect = false; + $ajax = Minz_Request::param ('ajax'); + if ($ajax) { + $this->view->_useLayout (false); + } + } + public function lastAction () { + $ajax = Minz_Request::param ('ajax'); + if (!$ajax && $this->redirect) { + Minz_Request::forward (array ( + 'c' => 'index', + 'a' => 'index', + 'params' => $this->params + ), true); + } else { + Minz_Request::_param ('ajax'); + } + } + + public function readAction () { + $this->redirect = true; + + $id = Minz_Request::param ('id'); + $is_read = Minz_Request::param ('is_read'); + $get = Minz_Request::param ('get'); + $nextGet = Minz_Request::param ('nextGet', $get); + $idMax = Minz_Request::param ('idMax', 0); + + $is_read = !!$is_read; + + $entryDAO = new FreshRSS_EntryDAO (); + if ($id == false) { + if (!$get) { + $entryDAO->markReadEntries ($idMax); + } else { + $typeGet = $get[0]; + $get = substr ($get, 2); + switch ($typeGet) { + case 'c': + $entryDAO->markReadCat ($get, $idMax); + break; + case 'f': + $entryDAO->markReadFeed ($get, $idMax); + break; + case 's': + $entryDAO->markReadEntries ($idMax, true); + break; + case 'a': + $entryDAO->markReadEntries ($idMax); + break; + } + if ($nextGet !== 'a') { + $this->params = array ('get' => $nextGet); + } + } + + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('feeds_marked_read') + ); + Minz_Session::_param ('notification', $notif); + } else { + $entryDAO->markRead ($id, $is_read); + } + } + + public function bookmarkAction () { + $this->redirect = true; + + $id = Minz_Request::param ('id'); + if ($id) { + $entryDAO = new FreshRSS_EntryDAO (); + $entryDAO->markFavorite ($id, Minz_Request::param ('is_favorite')); + } + } + + public function optimizeAction() { + @set_time_limit(300); + invalidateHttpCache(); + + // La table des entrées a tendance à grossir énormément + // Cette action permet d'optimiser cette table permettant de grapiller un peu de place + // Cette fonctionnalité n'est à appeler qu'occasionnellement + $entryDAO = new FreshRSS_EntryDAO(); + $entryDAO->optimizeTable(); + + invalidateHttpCache(); + + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('optimization_complete') + ); + Minz_Session::_param ('notification', $notif); + + Minz_Request::forward(array( + 'c' => 'configure', + 'a' => 'display' + ), true); + } +} diff --git a/app/Controllers/errorController.php b/app/Controllers/errorController.php new file mode 100644 index 000000000..d1c2f8fec --- /dev/null +++ b/app/Controllers/errorController.php @@ -0,0 +1,26 @@ +view->code = 'Error 403 - Forbidden'; + break; + case 404: + $this->view->code = 'Error 404 - Not found'; + break; + case 500: + $this->view->code = 'Error 500 - Internal Server Error'; + break; + case 503: + $this->view->code = 'Error 503 - Service Unavailable'; + break; + default: + $this->view->code = 'Error 404 - Not found'; + } + + $this->view->logs = Minz_Request::param ('logs'); + + Minz_View::prependTitle ($this->view->code . ' - '); + } +} diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php new file mode 100755 index 000000000..a85877724 --- /dev/null +++ b/app/Controllers/feedController.php @@ -0,0 +1,428 @@ +view->conf->token(); + $token_param = Minz_Request::param ('token', ''); + $token_is_ok = ($token != '' && $token == $token_param); + $action = Minz_Request::actionName (); + + if (login_is_conf ($this->view->conf) && + !is_logged () && + !($token_is_ok && $action == 'actualize')) { + Minz_Error::error ( + 403, + array ('error' => array (Minz_Translate::t ('access_denied'))) + ); + } + + $this->catDAO = new FreshRSS_CategoryDAO (); + $this->catDAO->checkDefault (); + } + + private static function entryDateComparer($e1, $e2) { + $d1 = $e1->date(true); + $d2 = $e2->date(true); + if ($d1 === $d2) { + return 0; + } + return ($d1 < $d2) ? -1 : 1; + } + + public function addAction () { + @set_time_limit(300); + + if (Minz_Request::isPost ()) { + $url = Minz_Request::param ('url_rss'); + $cat = Minz_Request::param ('category', false); + if ($cat === false) { + $def_cat = $this->catDAO->getDefault (); + $cat = $def_cat->id (); + } + + $user = Minz_Request::param ('username'); + $pass = Minz_Request::param ('password'); + $params = array (); + + $transactionStarted = false; + try { + $feed = new FreshRSS_Feed ($url); + $feed->_category ($cat); + + $httpAuth = ''; + if ($user != '' || $pass != '') { + $httpAuth = $user . ':' . $pass; + } + $feed->_httpAuth ($httpAuth); + + $feed->load (); + + $feedDAO = new FreshRSS_FeedDAO (); + $values = array ( + 'url' => $feed->url (), + 'category' => $feed->category (), + 'name' => $feed->name (), + 'website' => $feed->website (), + 'description' => $feed->description (), + 'lastUpdate' => time (), + 'httpAuth' => $feed->httpAuth (), + ); + + if ($feedDAO->searchByUrl ($values['url'])) { + // on est déjà abonné à ce flux + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('already_subscribed', $feed->name ()) + ); + Minz_Session::_param ('notification', $notif); + } else { + $id = $feedDAO->addFeed ($values); + if (!$id) { + // problème au niveau de la base de données + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('feed_not_added', $feed->name ()) + ); + Minz_Session::_param ('notification', $notif); + } else { + $feed->_id ($id); + $feed->faviconPrepare(); + + $is_read = $this->view->conf->markUponReception() === 'yes' ? 1 : 0; + + $entryDAO = new FreshRSS_EntryDAO (); + $entries = $feed->entries (); + usort($entries, 'self::entryDateComparer'); + + // on calcule la date des articles les plus anciens qu'on accepte + $nb_month_old = $this->view->conf->oldEntries (); + $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + + $transactionStarted = true; + $feedDAO->beginTransaction (); + // on ajoute les articles en masse sans vérification + foreach ($entries as $entry) { + if ($entry->date (true) >= $date_min || + $feed->keepHistory ()) { + $values = $entry->toArray (); + $values['id_feed'] = $feed->id (); + $values['id'] = min(time(), $entry->date (true)) . uSecString(); + $values['is_read'] = $is_read; + $entryDAO->addEntry ($values); + } + } + $feedDAO->updateLastUpdate ($feed->id ()); + $feedDAO->commit (); + $transactionStarted = false; + + // ok, ajout terminé + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('feed_added', $feed->name ()) + ); + Minz_Session::_param ('notification', $notif); + + // permet de rediriger vers la page de conf du flux + $params['id'] = $feed->id (); + } + } + } catch (FreshRSS_BadUrl_Exception $e) { + Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('invalid_url', $url) + ); + Minz_Session::_param ('notification', $notif); + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('internal_problem_feed') + ); + Minz_Session::_param ('notification', $notif); + } catch (Minz_FileNotExistException $e) { + // Répertoire de cache n'existe pas + Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('internal_problem_feed') + ); + Minz_Session::_param ('notification', $notif); + } + if ($transactionStarted) { + $feedDAO->rollBack (); + } + + Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => $params), true); + } + } + + public function truncateAction () { + if (Minz_Request::isPost ()) { + $id = Minz_Request::param ('id'); + $feedDAO = new FreshRSS_FeedDAO (); + $n = $feedDAO->truncate($id); + $notif = array( + 'type' => $n === false ? 'bad' : 'good', + 'content' => Minz_Translate::t ('n_entries_deleted', $n) + ); + Minz_Session::_param ('notification', $notif); + invalidateHttpCache(); + Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => array('id' => $id)), true); + } + } + + public function actualizeAction () { + @set_time_limit(300); + + $feedDAO = new FreshRSS_FeedDAO (); + $entryDAO = new FreshRSS_EntryDAO (); + + $id = Minz_Request::param ('id'); + $force = Minz_Request::param ('force', false); + + // on créé la liste des flux à mettre à actualiser + // si on veut mettre un flux à jour spécifiquement, on le met + // dans la liste, mais seul (permet d'automatiser le traitement) + $feeds = array (); + if ($id) { + $feed = $feedDAO->searchById ($id); + if ($feed) { + $feeds = array ($feed); + } + } else { + $feeds = $feedDAO->listFeedsOrderUpdate (); + } + + // on calcule la date des articles les plus anciens qu'on accepte + $nb_month_old = $this->view->conf->oldEntries (); + $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + + $i = 0; + $flux_update = 0; + foreach ($feeds as $feed) { + try { + $feed->load (); + $feed->faviconPrepare(); + $entries = $feed->entries (); + usort($entries, 'self::entryDateComparer'); + + $is_read = $this->view->conf->markUponReception() === 'yes' ? 1 : 0; + + //For this feed, check last n entry GUIDs already in database + $existingGuids = array_fill_keys ($entryDAO->listLastGuidsByFeed ($feed->id (), count($entries) + 10), 1); + + // On ne vérifie pas strictement que l'article n'est pas déjà en BDD + // La BDD refusera l'ajout car (id_feed, guid) doit être unique + $feedDAO->beginTransaction (); + foreach ($entries as $entry) { + if ((!isset ($existingGuids[$entry->guid ()])) && + ($entry->date (true) >= $date_min || + $feed->keepHistory ())) { + $values = $entry->toArray (); + //Use declared date at first import, otherwise use discovery date + $values['id'] = empty($existingGuids) ? min(time(), $entry->date (true)) . uSecString() : uTimeString(); + $values['is_read'] = $is_read; + $entryDAO->addEntry ($values); + } + } + + if ((!$feed->keepHistory()) && (rand(0, 30) === 1)) { + $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, count($entries) + 10); + Minz_Log::record ($nb . ' old entries cleaned in feed ' . $feed->id (), Minz_Log::DEBUG); + } + + // on indique que le flux vient d'être mis à jour en BDD + $feedDAO->updateLastUpdate ($feed->id ()); + $feedDAO->commit (); + $flux_update++; + } catch (FreshRSS_Feed_Exception $e) { + Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE); + $feedDAO->updateLastUpdate ($feed->id (), 1); + } + + // On arrête à 10 flux pour ne pas surcharger le serveur + // sauf si le paramètre $force est à vrai + $i++; + if ($i >= 10 && !$force) { + break; + } + } + + $url = array (); + if ($flux_update === 1) { + // on a mis un seul flux à jour + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('feed_actualized', $feed->name ()) + ); + } elseif ($flux_update > 1) { + // plusieurs flux on été mis à jour + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('n_feeds_actualized', $flux_update) + ); + } else { + // aucun flux n'a été mis à jour, oups + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('no_feed_actualized') + ); + } + + if ($i === 1) { + // Si on a voulu mettre à jour qu'un flux + // on filtre l'affichage par ce flux + $feed = reset ($feeds); + $url['params'] = array ('get' => 'f_' . $feed->id ()); + } + + if (Minz_Request::param ('ajax', 0) === 0) { + Minz_Session::_param ('notification', $notif); + Minz_Request::forward ($url, true); + } else { + // Une requête Ajax met un seul flux à jour. + // Comme en principe plusieurs requêtes ont lieu, + // on indique que "plusieurs flux ont été mis à jour". + // Cela permet d'avoir une notification plus proche du + // ressenti utilisateur + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('feeds_actualized') + ); + Minz_Session::_param ('notification', $notif); + // et on désactive le layout car ne sert à rien + $this->view->_useLayout (false); + } + } + + public function massiveImportAction () { + @set_time_limit(300); + + $entryDAO = new FreshRSS_EntryDAO (); + $feedDAO = new FreshRSS_FeedDAO (); + + $categories = Minz_Request::param ('categories', array (), true); + $feeds = Minz_Request::param ('feeds', array (), true); + + // on ajoute les catégories en masse dans une fonction à part + $this->addCategories ($categories); + + // on calcule la date des articles les plus anciens qu'on accepte + $nb_month_old = $this->view->conf->oldEntries (); + $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + + // la variable $error permet de savoir si une erreur est survenue + // Le but est de ne pas arrêter l'import même en cas d'erreur + // L'utilisateur sera mis au courant s'il y a eu des erreurs, mais + // ne connaîtra pas les détails. Ceux-ci seront toutefois logguées + $error = false; + $i = 0; + foreach ($feeds as $feed) { + try { + $values = array ( + 'id' => $feed->id (), + 'url' => $feed->url (), + 'category' => $feed->category (), + 'name' => $feed->name (), + 'website' => $feed->website (), + 'description' => $feed->description (), + 'lastUpdate' => 0, + 'httpAuth' => $feed->httpAuth () + ); + + // ajout du flux que s'il n'est pas déjà en BDD + if (!$feedDAO->searchByUrl ($values['url'])) { + $id = $feedDAO->addFeed ($values); + if ($id) { + $feed->_id ($id); + $feed->faviconPrepare(); + } else { + $error = true; + } + } + } catch (FreshRSS_Feed_Exception $e) { + $error = true; + Minz_Log::record ($e->getMessage (), Minz_Log::WARNING); + } + } + + if ($error) { + $res = Minz_Translate::t ('feeds_imported_with_errors'); + } else { + $res = Minz_Translate::t ('feeds_imported'); + } + + $notif = array ( + 'type' => 'good', + 'content' => $res + ); + Minz_Session::_param ('notification', $notif); + Minz_Session::_param ('actualize_feeds', true); + + // et on redirige vers la page d'accueil + Minz_Request::forward (array ( + 'c' => 'index', + 'a' => 'index' + ), true); + } + + public function deleteAction () { + if (Minz_Request::isPost ()) { + $type = Minz_Request::param ('type', 'feed'); + $id = Minz_Request::param ('id'); + + $feedDAO = new FreshRSS_FeedDAO (); + if ($type == 'category') { + if ($feedDAO->deleteFeedByCategory ($id)) { + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('category_emptied') + ); + //TODO: Delete old favicons + } else { + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('error_occured') + ); + } + } else { + if ($feedDAO->deleteFeed ($id)) { + $notif = array ( + 'type' => 'good', + 'content' => Minz_Translate::t ('feed_deleted') + ); + Feed::faviconDelete($id); + } else { + $notif = array ( + 'type' => 'bad', + 'content' => Minz_Translate::t ('error_occured') + ); + } + } + + Minz_Session::_param ('notification', $notif); + + if ($type == 'category') { + Minz_Request::forward (array ('c' => 'configure', 'a' => 'categorize'), true); + } else { + Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed'), true); + } + } + } + + private function addCategories ($categories) { + $catDAO = new FreshRSS_CategoryDAO (); + + foreach ($categories as $cat) { + if (!$catDAO->searchByName ($cat->name ())) { + $values = array ( + 'id' => $cat->id (), + 'name' => $cat->name (), + 'color' => $cat->color () + ); + $catDAO->addCategory ($values); + } + } + } +} diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php new file mode 100755 index 000000000..92070590a --- /dev/null +++ b/app/Controllers/indexController.php @@ -0,0 +1,277 @@ +entryDAO = new FreshRSS_EntryDAO (); + $this->feedDAO = new FreshRSS_FeedDAO (); + $this->catDAO = new FreshRSS_CategoryDAO (); + } + + public function indexAction () { + $output = Minz_Request::param ('output'); + + $token = $this->view->conf->token(); + $token_param = Minz_Request::param ('token', ''); + $token_is_ok = ($token != '' && $token === $token_param); + + // check if user is log in + if(login_is_conf ($this->view->conf) && + !is_logged() && + $this->view->conf->anonAccess() === 'no' && + !($output === 'rss' && $token_is_ok)) { + return; + } + + // construction of RSS url of this feed + $params = Minz_Request::params (); + $params['output'] = 'rss'; + if (isset ($params['search'])) { + $params['search'] = urlencode ($params['search']); + } + if (login_is_conf($this->view->conf) && + $this->view->conf->anonAccess() === 'no' && + $token != '') { + $params['token'] = $token; + } + $this->view->rss_url = array ( + 'c' => 'index', + 'a' => 'index', + 'params' => $params + ); + + if ($output === 'rss') { + // no layout for RSS output + $this->view->_useLayout (false); + header('Content-Type: application/rss+xml; charset=utf-8'); + } else { + Minz_View::appendScript (Minz_Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); + + if ($output === 'global') { + Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); + } + } + + $this->view->cat_aside = $this->catDAO->listCategories (); + $this->view->nb_favorites = $this->entryDAO->countUnreadReadFavorites (); + $this->view->currentName = ''; + + $this->view->get_c = ''; + $this->view->get_f = ''; + + $get = Minz_Request::param ('get', 'a'); + $getType = $get[0]; + $getId = substr ($get, 2); + if (!$this->checkAndProcessType ($getType, $getId)) { + Minz_Log::record ('Not found [' . $getType . '][' . $getId . ']', Minz_Log::DEBUG); + Minz_Error::error ( + 404, + array ('error' => array (Minz_Translate::t ('page_not_found'))) + ); + return; + } + + $this->view->nb_not_read = HelperCategory::CountUnreads($this->view->cat_aside, 1); + + // mise à jour des titres + $this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title(); + if ($this->view->nb_not_read > 0) { + Minz_View::appendTitle (' (' . $this->view->nb_not_read . ')'); + } + Minz_View::prependTitle ( + $this->view->currentName . + ($this->nb_not_read_cat > 0 ? ' (' . $this->nb_not_read_cat . ')' : '') . + ' - ' + ); + + // On récupère les différents éléments de filtrage + $this->view->state = $state = Minz_Request::param ('state', $this->view->conf->defaultView ()); + $filter = Minz_Request::param ('search', ''); + if (!empty($filter)) { + $state = 'all'; //Search always in read and unread articles + } + $this->view->order = $order = Minz_Request::param ('order', $this->view->conf->sortOrder ()); + $nb = Minz_Request::param ('nb', $this->view->conf->postsPerPage ()); + $first = Minz_Request::param ('next', ''); + + if ($state === 'not_read') { //Any unread article in this category at all? + switch ($getType) { + case 'a': + $hasUnread = $this->view->nb_not_read > 0; + break; + case 's': + $hasUnread = $this->view->nb_favorites['unread'] > 0; + break; + case 'c': + $hasUnread = (!isset($this->view->cat_aside[$getId])) || ($this->view->cat_aside[$getId]->nbNotRead() > 0); + break; + case 'f': + $myFeed = HelperCategory::findFeed($this->view->cat_aside, $getId); + $hasUnread = ($myFeed === null) || ($myFeed->nbNotRead() > 0); + break; + default: + $hasUnread = true; + break; + } + if (!$hasUnread) { + $this->view->state = $state = 'all'; + } + } + + // on calcule la date des articles les plus anciens qu'on affiche + $nb_month_old = $this->view->conf->oldEntries (); + $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + + try { + $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min); + + // Si on a récupéré aucun article "non lus" + // on essaye de récupérer tous les articles + if ($state === 'not_read' && empty($entries)) { //TODO: Remove in v0.8 + Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); + $this->view->state = 'all'; + $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min); + } + + if (count($entries) <= $nb) { + $this->view->nextId = ''; + } else { //We have more elements for pagination + $lastEntry = array_pop($entries); + $this->view->nextId = $lastEntry->id(); + } + + $this->view->entries = $entries; + } catch (FreshRSS_EntriesGetter_Exception $e) { + Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE); + Minz_Error::error ( + 404, + array ('error' => array (Minz_Translate::t ('page_not_found'))) + ); + } + } + + /* + * Vérifie que la catégorie / flux sélectionné existe + * + Initialise correctement les variables de vue get_c et get_f + * + Met à jour la variable $this->nb_not_read_cat + */ + private function checkAndProcessType ($getType, $getId) { + switch ($getType) { + case 'a': + $this->view->currentName = Minz_Translate::t ('your_rss_feeds'); + $this->view->get_c = $getType; + return true; + case 's': + $this->view->currentName = Minz_Translate::t ('your_favorites'); + $this->view->get_c = $getType; + return true; + case 'c': + $cat = isset($this->view->cat_aside[$getId]) ? $this->view->cat_aside[$getId] : null; + if ($cat === null) { + $cat = $this->catDAO->searchById ($getId); + } + if ($cat) { + $this->view->currentName = $cat->name (); + $this->nb_not_read_cat = $cat->nbNotRead (); + $this->view->get_c = $getId; + return true; + } else { + return false; + } + case 'f': + $feed = HelperCategory::findFeed($this->view->cat_aside, $getId); + if (empty($feed)) { + $feed = $this->feedDAO->searchById ($getId); + } + if ($feed) { + $this->view->currentName = $feed->name (); + $this->nb_not_read_cat = $feed->nbNotRead (); + $this->view->get_f = $getId; + $this->view->get_c = $feed->category (); + return true; + } else { + return false; + } + default: + return false; + } + } + + public function aboutAction () { + Minz_View::prependTitle (Minz_Translate::t ('about') . ' - '); + } + + public function logsAction () { + if (login_is_conf ($this->view->conf) && !is_logged ()) { + Minz_Error::error ( + 403, + array ('error' => array (Minz_Translate::t ('access_denied'))) + ); + } + + Minz_View::prependTitle (Minz_Translate::t ('logs') . ' - '); + + if (Minz_Request::isPost ()) { + file_put_contents(LOG_PATH . '/application.log', ''); + } + + $logs = array(); + try { + $logDAO = new FreshRSS_LogDAO (); + $logs = $logDAO->lister (); + $logs = array_reverse ($logs); + } catch (Minz_FileNotExistException $e) { + + } + + //gestion pagination + $page = Minz_Request::param ('page', 1); + $this->view->logsPaginator = new Minz_Paginator ($logs); + $this->view->logsPaginator->_nbItemsPerPage (50); + $this->view->logsPaginator->_currentPage ($page); + } + + public function loginAction () { + $this->view->_useLayout (false); + + $url = 'https://verifier.login.persona.org/verify'; + $assert = Minz_Request::param ('assertion'); + $params = 'assertion=' . $assert . '&audience=' . + urlencode (Minz_Url::display (null, 'php', true)); + $ch = curl_init (); + $options = array ( + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_POST => 2, + CURLOPT_POSTFIELDS => $params + ); + curl_setopt_array ($ch, $options); + $result = curl_exec ($ch); + curl_close ($ch); + + $res = json_decode ($result, true); + if ($res['status'] === 'okay' && $res['email'] === $this->view->conf->mailLogin ()) { + Minz_Session::_param ('mail', $res['email']); + invalidateHttpCache(); + } else { + $res = array (); + $res['status'] = 'failure'; + $res['reason'] = Minz_Translate::t ('invalid_login'); + } + + header('Content-Type: application/json; charset=UTF-8'); + $this->view->res = json_encode ($res); + } + + public function logoutAction () { + $this->view->_useLayout (false); + Minz_Session::_param ('mail'); + invalidateHttpCache(); + } +} diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php new file mode 100755 index 000000000..e7e25f656 --- /dev/null +++ b/app/Controllers/javascriptController.php @@ -0,0 +1,13 @@ +view->_useLayout (false); + header('Content-type: text/javascript'); + } + + public function actualizeAction () { + $feedDAO = new FreshRSS_FeedDAO (); + $this->view->feeds = $feedDAO->listFeeds (); + } +} -- cgit v1.2.3 From ce3801e5acf22e64cb53cfad21ced1b962674a91 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 15 Dec 2013 12:17:20 +0100 Subject: Message debug nettoyage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Message dans le log uniquement lorsque des articles ont été supprimés https://github.com/marienfressinaud/FreshRSS/issues/323 --- app/Controllers/feedController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index a85877724..18476d559 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -229,7 +229,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if ((!$feed->keepHistory()) && (rand(0, 30) === 1)) { $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, count($entries) + 10); - Minz_Log::record ($nb . ' old entries cleaned in feed ' . $feed->id (), Minz_Log::DEBUG); + if ($nb > 0) { + Minz_Log::record ($nb . ' old entries cleaned in feed ' . $feed->id (), Minz_Log::DEBUG); + } } // on indique que le flux vient d'être mis à jour en BDD -- cgit v1.2.3 From 529d6bcd15f7351cb7bdcf2f74c6a44930b0de55 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 16 Dec 2013 00:50:24 +0100 Subject: SQL : performances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tentative de reformulation de la requête principale pour améliorer les performances. Utilisation d'une sous-jointure qui retourne uniquement e.id. Sur mon serveur avec 13000 articles, la requête de la page d'accueil sans article non lu mettait 1.38s avant le patch, contre 0.08s après (en désactivant bien sûr le cache SQL). Il faudra re-tester et tenter d'autres optimisations (notamment sur les index) avec un nombre d'articles plus important. Avant : SELECT SQL_NO_CACHE e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags FROM `freshrss_alex_entry` e INNER JOIN `freshrss_alex_feed` f ON e.id_feed = f.id WHERE f.priority > 0 AND (e.id >= 1371597014000000 OR e.is_favorite = 1 OR f.keep_history = 1) ORDER BY e.id DESC LIMIT 33; Après : SELECT SQL_NO_CACHE e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags FROM `freshrss_alex_entry` e INNER JOIN (SELECT e1.id FROM `freshrss_alex_entry` e1 INNER JOIN `freshrss_alex_feed` f ON e1.id_feed = f.id WHERE f.priority > 0 AND (e1.id >= 1371597014000000 OR e1.is_favorite = 1 OR f.keep_history = 1) ORDER BY e1.id DESC LIMIT 33) e2 ON e2.id = e.id ORDER BY e.id DESC; --- app/Controllers/indexController.php | 4 +++- app/Models/EntryDAO.php | 30 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 92070590a..cc474302e 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -124,9 +124,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { } } + $today = @strtotime('today'); + // on calcule la date des articles les plus anciens qu'on affiche $nb_month_old = $this->view->conf->oldEntries (); - $date_min = time () - (3600 * 24 * 30 * $nb_month_old); + $date_min = $today - (3600 * 24 * 30 * $nb_month_old); //Do not use a fast changing value such as time() to allow SQL caching try { $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index f80fe9b77..5a34573db 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -269,7 +269,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $joinFeed = true; break; case 's': - $where .= 'e.is_favorite = 1 '; + $where .= 'e1.is_favorite = 1 '; break; case 'c': $where .= 'f.category = ? '; @@ -277,7 +277,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $joinFeed = true; break; case 'f': - $where .= 'e.id_feed = ? '; + $where .= 'e1.id_feed = ? '; $values[] = intval($id); break; default: @@ -287,10 +287,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { case 'all': break; case 'not_read': - $where .= 'AND e.is_read = 0 '; + $where .= 'AND e1.is_read = 0 '; break; case 'read': - $where .= 'AND e.is_read = 1 '; + $where .= 'AND e1.is_read = 1 '; break; default: throw new FreshRSS_EntriesGetter_Exception ('Bad state in Entry->listByType: [' . $state . ']!'); @@ -303,10 +303,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { throw new FreshRSS_EntriesGetter_Exception ('Bad order in Entry->listByType: [' . $order . ']!'); } if ($firstId !== '') { - $where .= 'AND e.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; + $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; } if (($date_min > 0) && ($type !== 's')) { - $where .= 'AND (e.id >= ' . $date_min . '000000 OR e.is_favorite = 1 OR f.keep_history = 1) '; + $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR f.keep_history = 1) '; $joinFeed = true; } $terms = array_unique(explode(' ', trim($filter))); @@ -315,10 +315,10 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { foreach ($terms as $word) { if (!empty($word)) { if ($word[0] === '#' && isset($word[1])) { - $having .= 'AND e.tags LIKE ? '; + $having .= 'AND e1.tags LIKE ? '; $values[] = '%' . $word .'%'; } elseif (!empty($word)) { - $having .= 'AND (e.title LIKE ? OR content LIKE ?) '; + $having .= 'AND (e1.title LIKE ? OR content LIKE ?) '; $values[] = '%' . $word .'%'; $values[] = '%' . $word .'%'; } @@ -327,15 +327,15 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags ' . 'FROM `' . $this->prefix . 'entry` e ' - . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' : '') - . 'WHERE ' . $where - . (empty($having) ? '' : 'HAVING' . substr($having, 3)) + . 'INNER JOIN (SELECT e1.id FROM `' . $this->prefix . 'entry` e1 ' + . ($joinFeed ? 'INNER JOIN `' . $this->prefix . 'feed` f ON e1.id_feed = f.id ' : '') + . 'WHERE ' . $where + . (empty($having) ? '' : 'HAVING' . substr($having, 3)) + . 'ORDER BY e1.id ' . $order + . ($limit > 0 ? ' LIMIT ' . $limit : '') //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ + . ') e2 ON e2.id = e.id ' . 'ORDER BY e.id ' . $order; - if ($limit > 0) { - $sql .= ' LIMIT ' . $limit; //TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ - } - $stm = $this->bd->prepare ($sql); $stm->execute ($values); -- cgit v1.2.3 From 847de9b3292ad854b281d7e12cc36ac93e745139 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 16 Dec 2013 00:54:13 +0100 Subject: PHP : performances fonction isDay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amélioration des performances de Entry->isDay() --- app/Controllers/indexController.php | 1 + app/Models/Entry.php | 27 +++++++++++++-------------- app/layout/nav_menu.phtml | 2 +- app/views/helpers/view/normal_view.phtml | 7 ++++--- 4 files changed, 19 insertions(+), 18 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index cc474302e..22c5683ea 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -125,6 +125,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } $today = @strtotime('today'); + $this->view->today = $today; // on calcule la date des articles les plus anciens qu'on affiche $nb_month_old = $this->view->conf->oldEntries (); diff --git a/app/Models/Entry.php b/app/Models/Entry.php index ba0fb48f4..983f94727 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -134,21 +134,20 @@ class FreshRSS_Entry extends Minz_Model { $this->tags = $value; } - public function isDay ($day) { + public function isDay ($day, $today) { $date = $this->dateAdded(true); - $today = @strtotime('today'); - $yesterday = $today - 86400; - - if ($day === FreshRSS_Days::TODAY && - $date >= $today && $date < $today + 86400) { - return true; - } elseif ($day === FreshRSS_Days::YESTERDAY && - $date >= $yesterday && $date < $yesterday + 86400) { - return true; - } elseif ($day === FreshRSS_Days::BEFORE_YESTERDAY && $date < $yesterday) { - return true; - } else { - return false; + switch ($day) { + case FreshRSS_Days::TODAY: + $tomorrow = $today + 86400; + return $date >= $today && $date < $tomorrow; + case FreshRSS_Days::YESTERDAY: + $yesterday = $today - 86400; + return $date >= $yesterday && $date < $today; + case FreshRSS_Days::BEFORE_YESTERDAY: + $yesterday = $today - 86400; + return $date < $yesterday; + default: + return false; } } diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 92a987aed..045f391f9 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -72,7 +72,7 @@
  • today; $one_week = $today - 604800; ?>
  • diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 094017957..d5328651d 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -21,24 +21,25 @@ if (!empty($this->entries)) { $facebook = $this->conf->sharing ('facebook'); $email = $this->conf->sharing ('email'); $print = $this->conf->sharing ('print'); + $today = $this->today; ?> entries as $item) { ?> - isDay (FreshRSS_Days::TODAY)) { ?> + isDay (FreshRSS_Days::TODAY, $today)) { ?>
    - currentName; ?>
    - isDay (FreshRSS_Days::YESTERDAY)) { ?> + isDay (FreshRSS_Days::YESTERDAY, $today)) { ?>
    - currentName; ?>
    - isDay (FreshRSS_Days::BEFORE_YESTERDAY)) { ?> + isDay (FreshRSS_Days::BEFORE_YESTERDAY, $today)) { ?>
    currentName; ?> -- cgit v1.2.3 From 6b7d96d0ea97579720ee6d560224cd80c2329d07 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 19 Dec 2013 21:19:45 +0100 Subject: Refactorisation : correction classes oubliées MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/indexController.php | 6 +++--- app/Models/CategoryDAO.php | 16 +++++++--------- app/Models/EntryDAO.php | 8 +++----- app/Models/FeedDAO.php | 12 +++++------- app/views/helpers/view/normal_view.phtml | 2 +- app/views/helpers/view/reader_view.phtml | 2 +- 6 files changed, 20 insertions(+), 26 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 22c5683ea..e3c253518 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -77,7 +77,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { return; } - $this->view->nb_not_read = HelperCategory::CountUnreads($this->view->cat_aside, 1); + $this->view->nb_not_read = FreshRSS_CategoryDAO::CountUnreads($this->view->cat_aside, 1); // mise à jour des titres $this->view->rss_title = $this->view->currentName . ' | ' . Minz_View::title(); @@ -112,7 +112,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $hasUnread = (!isset($this->view->cat_aside[$getId])) || ($this->view->cat_aside[$getId]->nbNotRead() > 0); break; case 'f': - $myFeed = HelperCategory::findFeed($this->view->cat_aside, $getId); + $myFeed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId); $hasUnread = ($myFeed === null) || ($myFeed->nbNotRead() > 0); break; default: @@ -188,7 +188,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { return false; } case 'f': - $feed = HelperCategory::findFeed($this->view->cat_aside, $getId); + $feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId); if (empty($feed)) { $feed = $this->feedDAO->searchById ($getId); } diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 793e593c3..3a810e9f0 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -60,7 +60,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $cat = HelperCategory::daoToCategory ($res); + $cat = self::daoToCategory ($res); if (isset ($cat[0])) { return $cat[0]; @@ -76,7 +76,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $cat = HelperCategory::daoToCategory ($res); + $cat = self::daoToCategory ($res); if (isset ($cat[0])) { return $cat[0]; @@ -96,12 +96,12 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { . 'ORDER BY c.name, f.name'; $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperCategory::daoToCategoryPrepopulated ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToCategoryPrepopulated ($stm->fetchAll (PDO::FETCH_ASSOC)); } else { $sql = 'SELECT * FROM `' . $this->prefix . 'category` ORDER BY name'; $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperCategory::daoToCategory ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToCategory ($stm->fetchAll (PDO::FETCH_ASSOC)); } } @@ -111,7 +111,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { $stm->execute (); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $cat = HelperCategory::daoToCategory ($res); + $cat = self::daoToCategory ($res); if (isset ($cat[0])) { return $cat[0]; @@ -164,9 +164,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { return $res[0]['count']; } -} -class HelperCategory { public static function findFeed($categories, $feed_id) { foreach ($categories as $category) { foreach ($category->feeds () as $feed) { @@ -205,7 +203,7 @@ class HelperCategory { $cat = new FreshRSS_Category ( $previousLine['c_name'], isset($previousLine['c_color']) ? $previousLine['c_color'] : '', - HelperFeed::daoToFeed ($feedsDao, $previousLine['c_id']) + FreshRSS_FeedDAO::daoToFeed ($feedsDao, $previousLine['c_id']) ); $cat->_id ($previousLine['c_id']); $list[$previousLine['c_id']] = $cat; @@ -222,7 +220,7 @@ class HelperCategory { $cat = new FreshRSS_Category ( $previousLine['c_name'], isset($previousLine['c_color']) ? $previousLine['c_color'] : '', - HelperFeed::daoToFeed ($feedsDao, $previousLine['c_id']) + FreshRSS_FeedDAO::daoToFeed ($feedsDao, $previousLine['c_id']) ); $cat->_id ($previousLine['c_id']); $list[$previousLine['c_id']] = $cat; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index c9989622a..d8bc869ae 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -242,7 +242,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $entries = HelperEntry::daoToEntry ($res); + $entries = self::daoToEntry ($res); return isset ($entries[0]) ? $entries[0] : false; } @@ -255,7 +255,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $entries = HelperEntry::daoToEntry ($res); + $entries = self::daoToEntry ($res); return isset ($entries[0]) ? $entries[0] : false; } @@ -372,7 +372,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute ($values); - return HelperEntry::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function listLastGuidsByFeed($id, $n) { @@ -430,9 +430,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute (); } -} -class HelperEntry { public static function daoToEntry ($listDAO) { $list = array (); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 8f59b1c76..9ebea4a47 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -158,7 +158,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $feed = HelperFeed::daoToFeed ($res); + $feed = self::daoToFeed ($res); if (isset ($feed[$id])) { return $feed[$id]; @@ -174,7 +174,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm->execute ($values); $res = $stm->fetchAll (PDO::FETCH_ASSOC); - $feed = current (HelperFeed::daoToFeed ($res)); + $feed = current (self::daoToFeed ($res)); if (isset ($feed)) { return $feed; @@ -188,7 +188,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperFeed::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function listFeedsOrderUpdate () { @@ -196,7 +196,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm = $this->bd->prepare ($sql); $stm->execute (); - return HelperFeed::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function listByCategory ($cat) { @@ -207,7 +207,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $stm->execute ($values); - return HelperFeed::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); + return self::daoToFeed ($stm->fetchAll (PDO::FETCH_ASSOC)); } public function countEntries ($id) { @@ -299,9 +299,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { return false; } } -} -class HelperFeed { public static function daoToFeed ($listDAO, $catID = null) { $list = array (); diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index d5328651d..f21a2bdd9 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -62,7 +62,7 @@ if (!empty($this->entries)) { ?>cat_aside, $item->feed ()); //We most likely already have the feed object in cache + $feed = FreshRSS_CategoryDAO::findFeed($this->cat_aside, $item->feed ()); //We most likely already have the feed object in cache if (empty($feed)) $feed = $item->feed (true); ?>
  • ✇ name(); ?>
  • diff --git a/app/views/helpers/view/reader_view.phtml b/app/views/helpers/view/reader_view.phtml index 29b2be04c..e28af6ade 100644 --- a/app/views/helpers/view/reader_view.phtml +++ b/app/views/helpers/view/reader_view.phtml @@ -11,7 +11,7 @@ if (!empty($this->entries)) {
    cat_aside, $item->feed ()); //We most likely already have the feed object in cache + $feed = FreshRSS_CategoryDAO::findFeed($this->cat_aside, $item->feed ()); //We most likely already have the feed object in cache if (empty($feed)) $feed = $item->feed (true); ?> -- cgit v1.2.3 From c013ecd0ae37db685d38b4a743f14bd55e437cf6 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 19 Dec 2013 21:34:13 +0100 Subject: Refactorisation : Minz_Translate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Un préfixe Minz_ oublié --- app/Controllers/configureController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 62fead315..ef03d9e1d 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -363,7 +363,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { // au niveau de la vue, permet de ne pas voir un flux sélectionné dans la liste $this->view->flux = false; - Minz_View::prependTitle (Translate::t ('import_export_opml') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('import_export_opml') . ' - '); } public function shortcutAction () { -- cgit v1.2.3 From 1c0e7a49528d1fb972a0fa0e9551d70dc5636442 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 19 Dec 2013 21:53:21 +0100 Subject: Refactorisation : FreshRSS_Feed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Un préfixe FreshRSS_ oublié --- app/Controllers/feedController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 18476d559..27b76dd42 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -394,7 +394,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { 'type' => 'good', 'content' => Minz_Translate::t ('feed_deleted') ); - Feed::faviconDelete($id); + FreshRSS_Feed::faviconDelete($id); } else { $notif = array ( 'type' => 'bad', -- cgit v1.2.3 From 3dc50cbd6627f9dfeb35c8e656eaf35f1f77495a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 19 Dec 2013 23:32:24 +0100 Subject: Compatibilité contenu HTML pour Feed->description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémente https://github.com/marienfressinaud/FreshRSS/issues/325 --- app/Controllers/configureController.php | 2 +- lib/lib_rss.php | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index ef03d9e1d..8d3e02d3e 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -94,7 +94,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } else { if (Minz_Request::isPost () && $this->view->flux) { $name = Minz_Request::param ('name', ''); - $description = Minz_Request::param('description', ''); + $description = sanitizeHTML(Minz_Request::param('description', '', true)); $website = Minz_Request::param('website', ''); $url = Minz_Request::param('url', ''); $hist = Minz_Request::param ('keep_history', 'no'); diff --git a/lib/lib_rss.php b/lib/lib_rss.php index c7b8b4beb..4ef06ddbc 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -136,6 +136,14 @@ function html_only_entity_decode($text) { return strtr($text, $htmlEntitiesOnly); } +function sanitizeHTML($data) { + static $simplePie = null; + if ($simplePie == null) { + $simplePie = new SimplePie(); + } + return html_only_entity_decode($simplePie->sanitize->sanitize($data, SIMPLEPIE_CONSTRUCT_MAYBE_HTML)); +} + function opml_import ($xml) { $xml = html_only_entity_decode($xml); //!\ Assume UTF-8 @@ -176,7 +184,7 @@ function opml_import ($xml) { // alors qu'il existe déjà la catégorie X mais avec l'id Z // Y ne sera pas ajouté et le flux non plus vu que l'id // de sa catégorie n'exisera pas - $title = htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); + $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); $catDAO = new FreshRSS_CategoryDAO (); $cat = $catDAO->searchByName ($title); if ($cat === false) { @@ -221,22 +229,22 @@ function getFeedsOutline ($outline, $cat_id) { function getFeed ($outline, $cat_id) { $url = (string) $outline['xmlUrl']; - $url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8'); + $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); $title = ''; if (isset ($outline['text'])) { $title = (string) $outline['text']; } elseif (isset ($outline['title'])) { $title = (string) $outline['title']; } - $title = htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); + $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); $feed = new FreshRSS_Feed ($url); $feed->_category ($cat_id); $feed->_name ($title); if (isset($outline['htmlUrl'])) { - $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_QUOTES, 'UTF-8')); + $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_COMPAT, 'UTF-8')); } if (isset($outline['description'])) { - $feed->_description((string)$outline['description']); + $feed->_description(sanitizeHTML((string)$outline['description'])); } return $feed; } -- cgit v1.2.3 From 87bfa195a6ff4ff73baadd3c04b7b6f28c9f9b73 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 24 Dec 2013 01:21:11 +0100 Subject: Permet de configurer plus finement le nombre d’articles minimum à conserver par flux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG | 1 + app/Controllers/feedController.php | 20 ++++++++------------ app/Models/EntryDAO.php | 2 +- app/Models/Feed.php | 17 +++++------------ app/Models/FeedDAO.php | 4 ++-- app/i18n/en.php | 7 +++++-- app/i18n/fr.php | 8 ++++++-- app/views/configure/display.phtml | 29 +++++++++++++++++++++-------- app/views/configure/feed.phtml | 22 ++++++++++++++++++---- public/install.php | 2 +- 10 files changed, 68 insertions(+), 44 deletions(-) (limited to 'app/Controllers') diff --git a/CHANGELOG b/CHANGELOG index af6936204..0c816dbd7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ * Améliorations partage vers Shaarli, Poche, Diaspora*, Facebook, Twitter, Google+, courriel * Permet la suppression de tous les articles d’un flux * Option pour marquer les articles comme lus dès la réception + * Permet de configurer plus finement le nombre d’articles minimum à conserver par flux * Permet de modifier la description et l’adresse d’un flux RSS ainsi que le site Web associé * Nouveau raccourci pour ouvrir/fermer un article (‘c’ par défaut) * Bouton pour effacer les logs diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 27b76dd42..e7d9c97c3 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -102,14 +102,11 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO->beginTransaction (); // on ajoute les articles en masse sans vérification foreach ($entries as $entry) { - if ($entry->date (true) >= $date_min || - $feed->keepHistory ()) { - $values = $entry->toArray (); - $values['id_feed'] = $feed->id (); - $values['id'] = min(time(), $entry->date (true)) . uSecString(); - $values['is_read'] = $is_read; - $entryDAO->addEntry ($values); - } + $values = $entry->toArray (); + $values['id_feed'] = $feed->id (); + $values['id'] = min(time(), $entry->date (true)) . uSecString(); + $values['is_read'] = $is_read; + $entryDAO->addEntry ($values); } $feedDAO->updateLastUpdate ($feed->id ()); $feedDAO->commit (); @@ -217,8 +214,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO->beginTransaction (); foreach ($entries as $entry) { if ((!isset ($existingGuids[$entry->guid ()])) && - ($entry->date (true) >= $date_min || - $feed->keepHistory ())) { + ($entry->date (true) >= $date_min)) { $values = $entry->toArray (); //Use declared date at first import, otherwise use discovery date $values['id'] = empty($existingGuids) ? min(time(), $entry->date (true)) . uSecString() : uTimeString(); @@ -227,8 +223,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - if ((!$feed->keepHistory()) && (rand(0, 30) === 1)) { - $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, count($entries) + 10); + if (($feed->keepHistory() >= 0) && (rand(0, 30) === 1)) { + $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feed->keepHistory(), count($entries) + 10)); if ($nb > 0) { Minz_Log::record ($nb . ' old entries cleaned in feed ' . $feed->id (), Minz_Log::DEBUG); } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index c6bd5c404..f0207e96d 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -307,7 +307,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; } if (($date_min > 0) && ($type !== 's')) { - $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR f.keep_history = 1) '; + $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR f.keep_history <> 0) '; $joinFeed = true; } $search = ''; diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 70efb0fa3..5bdf5e6d7 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -15,7 +15,7 @@ class FreshRSS_Feed extends Minz_Model { private $pathEntries = ''; private $httpAuth = ''; private $error = false; - private $keep_history = false; + private $keep_history = 0; public function __construct ($url, $validate=true) { if ($validate) { @@ -163,19 +163,12 @@ class FreshRSS_Feed extends Minz_Model { $this->httpAuth = $value; } public function _error ($value) { - if ($value) { - $value = true; - } else { - $value = false; - } - $this->error = $value; + $this->error = (bool)$value; } public function _keepHistory ($value) { - if ($value) { - $value = true; - } else { - $value = false; - } + $value = intval($value); + $value = min($value, 1000000); + $value = max($value, -1); $this->keep_history = $value; } public function _nbNotRead ($value) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 9bd480544..451fc3850 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -193,7 +193,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } public function listFeedsOrderUpdate () { - $sql = 'SELECT * FROM `' . $this->prefix . 'feed` ORDER BY lastUpdate'; + $sql = 'SELECT id, url, pathEntries, httpAuth, keep_history FROM `' . $this->prefix . 'feed` ORDER BY lastUpdate'; $stm = $this->bd->prepare ($sql); $stm->execute (); @@ -326,7 +326,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $myFeed->_pathEntries (isset($dao['pathEntries']) ? $dao['pathEntries'] : ''); $myFeed->_httpAuth (isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : ''); $myFeed->_error ($dao['error']); - $myFeed->_keepHistory (isset($dao['keep_history']) ? $dao['keep_history'] : ''); + $myFeed->_keepHistory (isset($dao['keep_history']) ? $dao['keep_history'] : 0); $myFeed->_nbNotRead ($dao['cache_nbUnreads']); $myFeed->_nbEntries ($dao['cache_nbEntries']); if (isset ($dao['id'])) { diff --git a/app/i18n/en.php b/app/i18n/en.php index 9c30573e8..b6417d8db 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -137,7 +137,8 @@ return array ( 'feed_url' => 'Feed URL', 'articles' => 'articles', 'number_articles' => 'Number of articles', - 'keep_history' => 'Keep old articles?', + 'keep_history' => 'Minimum number of articles to keep', + 'keep_history_help' => 'Set to -1 to keep everything', 'categorize' => 'Store in a category', 'truncate' => 'Delete all articles', 'advanced' => 'Advanced', @@ -157,13 +158,15 @@ return array ( 'general_configuration' => 'General configuration', 'language' => 'Language', - 'delete_articles_every' => 'Remove articles after', 'month' => 'months', 'default_user' => 'Username of the default user (maximum 16 alphanumeric characters)', 'persona_connection_email' => 'Login mail address (use Mozilla Persona)', 'allow_anonymous' => 'Allow anonymous reading', 'auth_token' => 'Authentication token', 'explain_token' => 'Allows to access RSS output without authentication.
    %s?token=%s', + 'archiving_configuration' => 'Archiving configuration', + 'delete_articles_every' => 'Remove articles after', + 'archiving_configuration_help' => 'More options are available in the individual stream settings', 'reading_configuration' => 'Reading configuration', 'articles_per_page' => 'Number of articles per page', 'default_view' => 'Default view', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 85bbeb4b7..b27f29940 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -137,7 +137,8 @@ return array ( 'feed_url' => 'URL du flux', 'articles' => 'articles', 'number_articles' => 'Nombre d’articles', - 'keep_history' => 'Garder les vieux articles ?', + 'keep_history' => 'Nombre minimum d’articles à conserver', + 'keep_history_help' => 'Mettre à -1 pour tout conserver', 'categorize' => 'Ranger dans une catégorie', 'truncate' => 'Supprimer tous les articles', 'advanced' => 'Avancé', @@ -157,13 +158,16 @@ return array ( 'general_configuration' => 'Configuration générale', 'language' => 'Langue', - 'delete_articles_every' => 'Supprimer les articles après', + 'month' => 'mois', 'default_user' => 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)', 'persona_connection_email' => 'Adresse courriel de connexion (utilise Mozilla Persona)', 'allow_anonymous' => 'Autoriser la lecture anonyme', 'auth_token' => 'Jeton d’identification', 'explain_token' => 'Permet d’accéder à la sortie RSS sans besoin de s’authentifier.
    %s?output=rss&token=%s', + 'archiving_configuration' => 'Configuration de l’archivage', + 'delete_articles_every' => 'Supprimer les articles après', + 'archiving_configuration_help' => 'D’autres options sont disponibles dans la configuration individuelle des flux', 'reading_configuration' => 'Configuration de lecture', 'articles_per_page' => 'Nombre d’articles par page', 'default_view' => 'Vue par défaut', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index ad35d7c71..68ef26bbf 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -31,13 +31,6 @@
    -
    - -
    - -
    -
    -
    conf->mailLogin (); ?> @@ -59,7 +52,27 @@
    - + + + +
    + +
    + + + + + + + + + + +
    + +
    +
    +
    diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index 4504b8d76..3fe7d1b3a 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -52,6 +52,9 @@
    + + +
    @@ -64,10 +67,21 @@
    flux->nbEntries (); ?> - +
    +
    +
    + +
    + + + + + + + + + +
    diff --git a/public/install.php b/public/install.php index afef7e473..3885f143e 100644 --- a/public/install.php +++ b/public/install.php @@ -93,7 +93,7 @@ FROM `%1$scategory006` ORDER BY id2; INSERT IGNORE INTO `%2$sfeed` (url, category, name, website, description, priority, pathEntries, httpAuth, keep_history) -SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, keep_history +SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, -1 * keep_history FROM `%1$sfeed006` ORDER BY id2; -- cgit v1.2.3 From a4b890b67fb4d97c97a2b1b455c327ce4b905194 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 25 Dec 2013 14:29:29 +0100 Subject: Suite gestion plus fine de l'historique --- app/Controllers/configureController.php | 7 +------ app/Models/FeedDAO.php | 2 +- app/views/configure/display.phtml | 37 ++++++++++++++++++++++++++------- app/views/configure/feed.phtml | 12 +++++++---- 4 files changed, 39 insertions(+), 19 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 8d3e02d3e..b83501f0b 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -97,18 +97,13 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $description = sanitizeHTML(Minz_Request::param('description', '', true)); $website = Minz_Request::param('website', ''); $url = Minz_Request::param('url', ''); - $hist = Minz_Request::param ('keep_history', 'no'); + $keep_history = intval(Minz_Request::param ('keep_history', 0)); $cat = Minz_Request::param ('category', 0); $path = Minz_Request::param ('path_entries', ''); $priority = Minz_Request::param ('priority', 0); $user = Minz_Request::param ('http_user', ''); $pass = Minz_Request::param ('http_pass', ''); - $keep_history = false; - if ($hist == 'yes') { - $keep_history = true; - } - $httpAuth = ''; if ($user != '' || $pass != '') { $httpAuth = $user . ':' . $pass; diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 451fc3850..7d91a032a 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -193,7 +193,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { } public function listFeedsOrderUpdate () { - $sql = 'SELECT id, url, pathEntries, httpAuth, keep_history FROM `' . $this->prefix . 'feed` ORDER BY lastUpdate'; + $sql = 'SELECT id, name, url, pathEntries, httpAuth, keep_history FROM `' . $this->prefix . 'feed` ORDER BY lastUpdate'; $stm = $this->bd->prepare ($sql); $stm->execute (); diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 68ef26bbf..8995dc839 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -53,6 +53,13 @@
    +
    +
    + + +
    +
    +
    @@ -73,6 +80,13 @@
    +
    +
    + + +
    +
    +
    @@ -173,6 +187,13 @@
    +
    +
    + + +
    +
    +
    @@ -206,7 +227,14 @@ -
    conf->bottomlineLink () ? ' checked="checked"' : ''; ?> />
    +
    +
    + +
    +
    + + +
    @@ -220,12 +248,5 @@ - -
    -
    - - -
    -
    diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index 3fe7d1b3a..5940055ed 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -52,6 +52,12 @@ +
    +
    + + +
    +
    @@ -84,9 +90,9 @@ -
    - +
    +
    @@ -123,11 +129,9 @@
    -
    -
    -- cgit v1.2.3 From 06d4b8d10247146d9c6f7c78ff9fc584438dd8fe Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 25 Dec 2013 17:37:52 +0100 Subject: Option globale pour la taille minimale de l'historique par défaut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plus une réorganisation des options --- app/Controllers/configureController.php | 5 ++- app/Controllers/feedController.php | 9 +++-- app/Controllers/indexController.php | 5 +-- app/Models/Configuration.php | 14 +++++++- app/Models/ConfigurationDAO.php | 8 +++-- app/Models/EntryDAO.php | 8 +++-- app/Models/Feed.php | 4 +-- app/Models/FeedDAO.php | 4 +-- app/i18n/en.php | 8 +++-- app/i18n/fr.php | 8 +++-- app/views/configure/display.phtml | 35 ++++++++++++------- app/views/configure/feed.phtml | 62 ++++++++++++++++++--------------- public/install.php | 4 +-- public/themes/default/freshrss.css | 4 +++ public/themes/default/global.css | 3 ++ public/themes/flat-design/freshrss.css | 4 +++ public/themes/flat-design/global.css | 3 ++ 17 files changed, 124 insertions(+), 64 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index b83501f0b..762134dd0 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -97,7 +97,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $description = sanitizeHTML(Minz_Request::param('description', '', true)); $website = Minz_Request::param('website', ''); $url = Minz_Request::param('url', ''); - $keep_history = intval(Minz_Request::param ('keep_history', 0)); + $keep_history = intval(Minz_Request::param ('keep_history', -2)); $cat = Minz_Request::param ('category', 0); $path = Minz_Request::param ('path_entries', ''); $priority = Minz_Request::param ('priority', 0); @@ -160,6 +160,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $lazyload = Minz_Request::param ('lazyload', 'no'); $sort = Minz_Request::param ('sort_order', 'DESC'); $old = Minz_Request::param ('old_entries', 3); + $keepHistoryDefault = Minz_Request::param('keep_history_default', 0); $mail = Minz_Request::param ('mail_login', false); $anon = Minz_Request::param ('anon_access', 'no'); $token = Minz_Request::param ('token', $current_token); @@ -189,6 +190,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_lazyload ($lazyload); $this->view->conf->_sortOrder ($sort); $this->view->conf->_oldEntries ($old); + $this->view->conf->_keepHistoryDefault($keepHistoryDefault); $this->view->conf->_mailLogin ($mail); $this->view->conf->_anonAccess ($anon); $this->view->conf->_token ($token); @@ -221,6 +223,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'lazyload' => $this->view->conf->lazyload (), 'sort_order' => $this->view->conf->sortOrder (), 'old_entries' => $this->view->conf->oldEntries (), + 'keep_history_default' => $this->view->conf->keepHistoryDefault(), 'mail_login' => $this->view->conf->mailLogin (), 'anon_access' => $this->view->conf->anonAccess (), 'token' => $this->view->conf->token (), diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index e7d9c97c3..836044da6 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -223,8 +223,13 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - if (($feed->keepHistory() >= 0) && (rand(0, 30) === 1)) { - $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feed->keepHistory(), count($entries) + 10)); + $feedHistory = $feed->keepHistory(); + if ($feedHistory == -2) { //default + $feedHistory = $this->view->conf->keepHistoryDefault(); + } + + if (($feedHistory >= 0) && (rand(0, 30) === 1)) { + $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feedHistory, count($entries) + 10)); if ($nb > 0) { Minz_Log::record ($nb . ' old entries cleaned in feed ' . $feed->id (), Minz_Log::DEBUG); } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index e3c253518..6c0ba9058 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -130,16 +130,17 @@ class FreshRSS_index_Controller extends Minz_ActionController { // on calcule la date des articles les plus anciens qu'on affiche $nb_month_old = $this->view->conf->oldEntries (); $date_min = $today - (3600 * 24 * 30 * $nb_month_old); //Do not use a fast changing value such as time() to allow SQL caching + $keepHistoryDefault = $this->view->conf->keepHistoryDefault(); try { - $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min); + $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault); // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles if ($state === 'not_read' && empty($entries)) { //TODO: Remove in v0.8 Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $this->view->state = 'all'; - $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min); + $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault); } if (count($entries) <= $nb) { diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index d5f69601f..47509636f 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -14,6 +14,7 @@ class FreshRSS_Configuration extends Minz_Model { private $lazyload; private $sort_order; private $old_entries; + private $keep_history_default; private $shortcuts = array (); private $mail_login = ''; private $mark_when = array (); @@ -44,6 +45,7 @@ class FreshRSS_Configuration extends Minz_Model { $this->_lazyload ($confDAO->lazyload); $this->_sortOrder ($confDAO->sort_order); $this->_oldEntries ($confDAO->old_entries); + $this->_keepHistoryDefault($confDAO->keep_history_default); $this->_shortcuts ($confDAO->shortcuts); $this->_mailLogin ($confDAO->mail_login); $this->_markWhen ($confDAO->mark_when); @@ -95,6 +97,9 @@ class FreshRSS_Configuration extends Minz_Model { public function oldEntries () { return $this->old_entries; } + public function keepHistoryDefault() { + return $this->keep_history_default; + } public function shortcuts () { return $this->shortcuts; } @@ -217,11 +222,18 @@ class FreshRSS_Configuration extends Minz_Model { } public function _oldEntries ($value) { if (ctype_digit ($value) && $value > 0) { - $this->old_entries = $value; + $this->old_entries = intval($value); } else { $this->old_entries = 3; } } + public function _keepHistoryDefault($value) { + if (ctype_digit($value) && $value >= -1) { + $this->keep_history_default = intval($value); + } else { + $this->keep_history_default = 0; + } + } public function _shortcuts ($values) { foreach ($values as $key => $value) { $this->shortcuts[$key] = $value; diff --git a/app/Models/ConfigurationDAO.php b/app/Models/ConfigurationDAO.php index 0eebf2d90..91210e701 100644 --- a/app/Models/ConfigurationDAO.php +++ b/app/Models/ConfigurationDAO.php @@ -10,6 +10,7 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { public $lazyload = 'yes'; public $sort_order = 'DESC'; public $old_entries = 3; + public $keep_history_default = 0; public $shortcuts = array ( 'mark_read' => 'r', 'mark_favorite' => 'f', @@ -62,7 +63,7 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { $this->language = $this->array['language']; } if (isset ($this->array['posts_per_page'])) { - $this->posts_per_page = $this->array['posts_per_page']; + $this->posts_per_page = intval($this->array['posts_per_page']); } if (isset ($this->array['view_mode'])) { $this->view_mode = $this->array['view_mode']; @@ -83,7 +84,10 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { $this->sort_order = $this->array['sort_order']; } if (isset ($this->array['old_entries'])) { - $this->old_entries = $this->array['old_entries']; + $this->old_entries = intval($this->array['old_entries']); + } + if (isset ($this->array['keep_history_default'])) { + $this->keep_history_default = intval($this->array['keep_history_default']); } if (isset ($this->array['shortcuts'])) { $this->shortcuts = array_merge ( diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index f0207e96d..14d3ddcff 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -260,7 +260,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { return isset ($entries[0]) ? $entries[0] : false; } - public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0) { + public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = '', $filter = '', $date_min = 0, $keepHistoryDefault = 0) { $where = ''; $joinFeed = false; $values = array(); @@ -307,7 +307,11 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo { $where .= 'AND e1.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; } if (($date_min > 0) && ($type !== 's')) { - $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR f.keep_history <> 0) '; + $where .= 'AND (e1.id >= ' . $date_min . '000000 OR e1.is_favorite = 1 OR (f.keep_history <> 0'; + if (intval($keepHistoryDefault) === 0) { + $where .= ' AND f.keep_history <> -2'; //default + } + $where .= ')) '; $joinFeed = true; } $search = ''; diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 5bdf5e6d7..ef554e083 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -15,7 +15,7 @@ class FreshRSS_Feed extends Minz_Model { private $pathEntries = ''; private $httpAuth = ''; private $error = false; - private $keep_history = 0; + private $keep_history = -2; public function __construct ($url, $validate=true) { if ($validate) { @@ -168,7 +168,7 @@ class FreshRSS_Feed extends Minz_Model { public function _keepHistory ($value) { $value = intval($value); $value = min($value, 1000000); - $value = max($value, -1); + $value = max($value, -2); $this->keep_history = $value; } public function _nbNotRead ($value) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 7d91a032a..c1d1f24e8 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -2,7 +2,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { public function addFeed ($valuesTmp) { - $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, 0)'; + $sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2)'; $stm = $this->bd->prepare ($sql); $values = array ( @@ -326,7 +326,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { $myFeed->_pathEntries (isset($dao['pathEntries']) ? $dao['pathEntries'] : ''); $myFeed->_httpAuth (isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : ''); $myFeed->_error ($dao['error']); - $myFeed->_keepHistory (isset($dao['keep_history']) ? $dao['keep_history'] : 0); + $myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : -2); $myFeed->_nbNotRead ($dao['cache_nbUnreads']); $myFeed->_nbEntries ($dao['cache_nbEntries']); if (isset ($dao['id'])) { diff --git a/app/i18n/en.php b/app/i18n/en.php index b6417d8db..ca72d885c 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -137,8 +137,9 @@ return array ( 'feed_url' => 'Feed URL', 'articles' => 'articles', 'number_articles' => 'Number of articles', + 'by_feed' => 'by feed', + 'by_default' => 'By default', 'keep_history' => 'Minimum number of articles to keep', - 'keep_history_help' => 'Set to -1 to keep everything', 'categorize' => 'Store in a category', 'truncate' => 'Delete all articles', 'advanced' => 'Advanced', @@ -164,10 +165,11 @@ return array ( 'allow_anonymous' => 'Allow anonymous reading', 'auth_token' => 'Authentication token', 'explain_token' => 'Allows to access RSS output without authentication.
    %s?token=%s', - 'archiving_configuration' => 'Archiving configuration', + 'login_configuration' => 'Login', + 'archiving_configuration' => 'Archiving', 'delete_articles_every' => 'Remove articles after', 'archiving_configuration_help' => 'More options are available in the individual stream settings', - 'reading_configuration' => 'Reading configuration', + 'reading_configuration' => 'Reading', 'articles_per_page' => 'Number of articles per page', 'default_view' => 'Default view', 'sort_order' => 'Sort order', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index b27f29940..053a97c8a 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -137,8 +137,9 @@ return array ( 'feed_url' => 'URL du flux', 'articles' => 'articles', 'number_articles' => 'Nombre d’articles', + 'by_feed' => 'par flux', + 'by_default' => 'Par défaut', 'keep_history' => 'Nombre minimum d’articles à conserver', - 'keep_history_help' => 'Mettre à -1 pour tout conserver', 'categorize' => 'Ranger dans une catégorie', 'truncate' => 'Supprimer tous les articles', 'advanced' => 'Avancé', @@ -165,10 +166,11 @@ return array ( 'allow_anonymous' => 'Autoriser la lecture anonyme', 'auth_token' => 'Jeton d’identification', 'explain_token' => 'Permet d’accéder à la sortie RSS sans besoin de s’authentifier.
    %s?output=rss&token=%s', - 'archiving_configuration' => 'Configuration de l’archivage', + 'login_configuration' => 'Identification', + 'archiving_configuration' => 'Archivage', 'delete_articles_every' => 'Supprimer les articles après', 'archiving_configuration_help' => 'D’autres options sont disponibles dans la configuration individuelle des flux', - 'reading_configuration' => 'Configuration de lecture', + 'reading_configuration' => 'Lecture', 'articles_per_page' => 'Nombre d’articles par page', 'default_view' => 'Vue par défaut', 'sort_order' => 'Ordre de tri', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 8995dc839..3280f657f 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -31,6 +31,15 @@ +
    +
    + + +
    +
    + + +
    conf->mailLogin (); ?> @@ -61,22 +70,22 @@
    - +

    +
    - - - - - - - - - - -
    - + +
    +
    +
    + +
    +
    diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index 5940055ed..e738ab64f 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -52,6 +52,15 @@ +
    + +
    + +
    +
    @@ -78,16 +87,11 @@
    - - - - - - - - - - +
    @@ -97,24 +101,7 @@
    - -
    - -
    - -
    -
    -
    - -
    - - -
    -
    - + flux->httpAuth (false); ?>
    @@ -131,7 +118,24 @@
    - + + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + +
    diff --git a/public/install.php b/public/install.php index 3885f143e..13abb010b 100644 --- a/public/install.php +++ b/public/install.php @@ -33,7 +33,7 @@ define ('SQL_FEED', 'CREATE TABLE IF NOT EXISTS `%1$sfeed` ( `pathEntries` varchar(511) DEFAULT NULL, `httpAuth` varchar(511) DEFAULT NULL, `error` boolean DEFAULT 0, - `keep_history` MEDIUMINT NOT NULL DEFAULT 0, + `keep_history` MEDIUMINT NOT NULL DEFAULT -2, -- v0.7, -2 = default `cache_nbEntries` int DEFAULT 0, -- v0.7 `cache_nbUnreads` int DEFAULT 0, -- v0.7 PRIMARY KEY (`id`), @@ -93,7 +93,7 @@ FROM `%1$scategory006` ORDER BY id2; INSERT IGNORE INTO `%2$sfeed` (url, category, name, website, description, priority, pathEntries, httpAuth, keep_history) -SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, -1 * keep_history +SELECT url, category2, name, website, description, priority, pathEntries, httpAuth, IF(keep_history = 1, -1, -2) FROM `%1$sfeed006` ORDER BY id2; diff --git a/public/themes/default/freshrss.css b/public/themes/default/freshrss.css index e3c4c3c3b..2b157b27a 100644 --- a/public/themes/default/freshrss.css +++ b/public/themes/default/freshrss.css @@ -667,6 +667,10 @@ padding:.5em; } +select.number option { + text-align:right; +} + @media(max-width: 840px) { .header, .aside .btn-important, diff --git a/public/themes/default/global.css b/public/themes/default/global.css index 1c554d2dc..440fc6e41 100644 --- a/public/themes/default/global.css +++ b/public/themes/default/global.css @@ -99,6 +99,9 @@ input, select, textarea { vertical-align: middle; box-shadow: 0 2px 2px #eee inset; } + option { + padding:0 .5em 0 .5em; + } input[type="radio"], input[type="checkbox"] { width: 15px !important; diff --git a/public/themes/flat-design/freshrss.css b/public/themes/flat-design/freshrss.css index fa1ed13e6..7e3f4c81a 100644 --- a/public/themes/flat-design/freshrss.css +++ b/public/themes/flat-design/freshrss.css @@ -662,6 +662,10 @@ body { padding:.5em; } +select.number option { + text-align:right; +} + @media(max-width: 840px) { .header, .aside .btn-important, diff --git a/public/themes/flat-design/global.css b/public/themes/flat-design/global.css index 8cf6412b3..90b59d002 100644 --- a/public/themes/flat-design/global.css +++ b/public/themes/flat-design/global.css @@ -101,6 +101,9 @@ input, select, textarea { vertical-align: middle; border-radius: 5px; } + option { + padding:0 .5em 0 .5em; + } input[type="radio"], input[type="checkbox"] { width: 15px !important; -- cgit v1.2.3 From 7eda2793bbc3210ae37aa66511fd7ad7661c2149 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 25 Dec 2013 19:53:09 +0100 Subject: Nouveau bouton pour lancer manuellement la purge des vieux articles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attention, si on supprime des articles qui sont encore dans les flux RSS, ils risquent de réapparaitre en cas de date manquante ou erronée, ou si l'utilisateur augmente la date d'expiration. Ce bouton est plus strict que la purge automatique qui conserve toujours au moins le même nombre d'articles que dans le flux RSS en cours + 10. --- app/Controllers/entryController.php | 41 +++++++++++++++++++++++++++++++++++++ app/Controllers/feedController.php | 2 +- app/Models/FeedDAO.php | 27 +++++++++++++----------- app/i18n/en.php | 2 ++ app/i18n/fr.php | 2 ++ app/views/configure/display.phtml | 1 + 6 files changed, 62 insertions(+), 13 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index a332ca8a9..1c3c56c4d 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -109,4 +109,45 @@ class FreshRSS_entry_Controller extends Minz_ActionController { 'a' => 'display' ), true); } + + public function purgeAction() { + @set_time_limit(300); + + $nb_month_old = max($this->view->conf->oldEntries(), 1); + $date_min = time() - (3600 * 24 * 30 * $nb_month_old); + + $feedDAO = new FreshRSS_FeedDAO(); + $feeds = $feedDAO->listFeedsOrderUpdate(); + $nbTotal = 0; + + invalidateHttpCache(); + + foreach ($feeds as $feed) { + $feedHistory = $feed->keepHistory(); + if ($feedHistory == -2) { //default + $feedHistory = $this->view->conf->keepHistoryDefault(); + } + if ($feedHistory >= 0) { + $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, $feedHistory); + if ($nb > 0) { + $nbTotal += $nb; + Minz_Log::record($nb . ' old entries cleaned in feed ' . $feed->id(), Minz_Log::DEBUG); + $feedDAO->updateLastUpdate($feed->id()); + } + } + } + + invalidateHttpCache(); + + $notif = array( + 'type' => 'good', + 'content' => Minz_Translate::t('purge_completed', $nbTotal) + ); + Minz_Session::_param('notification', $notif); + + Minz_Request::forward(array( + 'c' => 'configure', + 'a' => 'display' + ), true); + } } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 836044da6..04d0aa98b 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -192,7 +192,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } // on calcule la date des articles les plus anciens qu'on accepte - $nb_month_old = $this->view->conf->oldEntries (); + $nb_month_old = max($this->view->conf->oldEntries(), 1); $date_min = time () - (3600 * 24 * 30 * $nb_month_old); $i = 0; diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index c1d1f24e8..d517f9580 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -315,20 +315,23 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo { if (isset ($dao['id'])) { $key = $dao['id']; } + if ($catID === null) { + $catID = isset($dao['category']) ? $dao['category'] : 0; + } - $myFeed = new FreshRSS_Feed (isset($dao['url']) ? $dao['url'] : '', false); - $myFeed->_category ($catID === null ? $dao['category'] : $catID); - $myFeed->_name ($dao['name']); - $myFeed->_website ($dao['website'], false); - $myFeed->_description (isset($dao['description']) ? $dao['description'] : ''); - $myFeed->_lastUpdate (isset($dao['lastUpdate']) ? $dao['lastUpdate'] : 0); - $myFeed->_priority ($dao['priority']); - $myFeed->_pathEntries (isset($dao['pathEntries']) ? $dao['pathEntries'] : ''); - $myFeed->_httpAuth (isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : ''); - $myFeed->_error ($dao['error']); + $myFeed = new FreshRSS_Feed(isset($dao['url']) ? $dao['url'] : '', false); + $myFeed->_category(intval($catID)); + $myFeed->_name($dao['name']); + $myFeed->_website(isset($dao['website']) ? $dao['website'] : '', false); + $myFeed->_description(isset($dao['description']) ? $dao['description'] : ''); + $myFeed->_lastUpdate(isset($dao['lastUpdate']) ? $dao['lastUpdate'] : 0); + $myFeed->_priority(isset($dao['priority']) ? $dao['priority'] : 10); + $myFeed->_pathEntries(isset($dao['pathEntries']) ? $dao['pathEntries'] : ''); + $myFeed->_httpAuth(isset($dao['httpAuth']) ? base64_decode ($dao['httpAuth']) : ''); + $myFeed->_error(isset($dao['error']) ? $dao['error'] : 0); $myFeed->_keepHistory(isset($dao['keep_history']) ? $dao['keep_history'] : -2); - $myFeed->_nbNotRead ($dao['cache_nbUnreads']); - $myFeed->_nbEntries ($dao['cache_nbEntries']); + $myFeed->_nbNotRead(isset($dao['cache_nbUnreads']) ? $dao['cache_nbUnreads'] : 0); + $myFeed->_nbEntries(isset($dao['cache_nbEntries']) ? $dao['cache_nbEntries'] : 0); if (isset ($dao['id'])) { $myFeed->_id ($dao['id']); } diff --git a/app/i18n/en.php b/app/i18n/en.php index ca72d885c..498fccd14 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -168,6 +168,8 @@ return array ( 'login_configuration' => 'Login', 'archiving_configuration' => 'Archiving', 'delete_articles_every' => 'Remove articles after', + 'purge_now' => 'Purge now', + 'purge_completed' => 'Purge completed (%d articles deleted)', 'archiving_configuration_help' => 'More options are available in the individual stream settings', 'reading_configuration' => 'Reading', 'articles_per_page' => 'Number of articles per page', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 053a97c8a..c918daa44 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -169,6 +169,8 @@ return array ( 'login_configuration' => 'Identification', 'archiving_configuration' => 'Archivage', 'delete_articles_every' => 'Supprimer les articles après', + 'purge_now' => 'Purger maintenant', + 'purge_completed' => 'Purge effectuée (%d articles supprimés)', 'archiving_configuration_help' => 'D’autres options sont disponibles dans la configuration individuelle des flux', 'reading_configuration' => 'Lecture', 'articles_per_page' => 'Nombre d’articles par page', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 3280f657f..fca533752 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -76,6 +76,7 @@
    +  
    -- cgit v1.2.3 From 574d37bddc4e00ddbc6af57c28838e1dea6a730b Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 26 Dec 2013 19:58:17 +0100 Subject: Favicons compatibles multi-utilisateurs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 --- app/Controllers/feedController.php | 2 +- app/Models/CategoryDAO.php | 2 +- app/Models/Feed.php | 13 +++++++++---- lib/Minz/Configuration.php | 2 +- p/f.php | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 04d0aa98b..dc8a854a9 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -395,7 +395,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { 'type' => 'good', 'content' => Minz_Translate::t ('feed_deleted') ); - FreshRSS_Feed::faviconDelete($id); + //TODO: Delete old favicon } else { $notif = array ( 'type' => 'bad', diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 6b07ab063..1cc616ac0 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -90,7 +90,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo { if ($prePopulateFeeds) { $sql = 'SELECT c.id AS c_id, c.name AS c_name, ' . ($details ? 'c.color AS c_color, ' : '') - . ($details ? 'f.* ' : 'f.id, f.name, f.website, f.priority, f.error, f.cache_nbEntries, f.cache_nbUnreads ') + . ($details ? 'f.* ' : 'f.id, f.name, f.url, f.website, f.priority, f.error, f.cache_nbEntries, f.cache_nbUnreads ') . 'FROM `' . $this->prefix . 'category` c ' . 'LEFT OUTER JOIN `' . $this->prefix . 'feed` f ON f.category = c.id ' . 'GROUP BY f.id ' diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 3008e33d7..f9a586122 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -28,6 +28,11 @@ class FreshRSS_Feed extends Minz_Model { public function id () { return $this->id; } + + public function hash() { + return hash('crc32b', Minz_Configuration::salt() . $this->url); + } + public function url () { return $this->url; } @@ -96,7 +101,7 @@ class FreshRSS_Feed extends Minz_Model { return $this->nbNotRead; } public function faviconPrepare() { - $file = DATA_PATH . '/favicons/' . $this->id () . '.txt'; + $file = DATA_PATH . '/favicons/' . $this->hash() . '.txt'; if (!file_exists ($file)) { $t = $this->website; if (empty($t)) { @@ -105,13 +110,13 @@ class FreshRSS_Feed extends Minz_Model { file_put_contents($file, $t); } } - public static function faviconDelete($id) { - $path = DATA_PATH . '/favicons/' . $id; + public static function faviconDelete($hash) { + $path = DATA_PATH . '/favicons/' . $hash; @unlink($path . '.ico'); @unlink($path . '.txt'); } public function favicon () { - return Minz_Url::display ('/f.php?' . $this->id ()); + return Minz_Url::display ('/f.php?' . $this->hash()); } public function _id ($value) { diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 1b108dcdf..6c7206988 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -63,7 +63,7 @@ class Minz_Configuration { /* * Getteurs */ - public static function selApplication () { + public static function salt () { return self::$sel_application; } public static function environment () { diff --git a/p/f.php b/p/f.php index a56d58617..872b8cc2c 100644 --- a/p/f.php +++ b/p/f.php @@ -37,7 +37,7 @@ function download_favicon ($website, $dest) { } $id = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '0'; -if (!ctype_digit($id)) { +if (!ctype_xdigit($id)) { $id = '0'; } -- cgit v1.2.3 From 43fd0a543900866f4feaf0713e1726b98a60b22d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 26 Dec 2013 22:20:24 +0100 Subject: Mise à jour de f.url en base de données lorsque SimplePie découvre que l'adresse a changé MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Et correction problème favicon lorsque l'adresse du flux a changé du point de vue de SimplePie. Plus petites optimisations. --- app/Controllers/entryController.php | 2 +- app/Controllers/feedController.php | 27 ++++++++++----------------- app/Models/Feed.php | 18 ++++++++++++------ 3 files changed, 23 insertions(+), 24 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 1c3c56c4d..26f3422ca 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -131,7 +131,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $nb = $feedDAO->cleanOldEntries($feed->id(), $date_min, $feedHistory); if ($nb > 0) { $nbTotal += $nb; - Minz_Log::record($nb . ' old entries cleaned in feed ' . $feed->id(), Minz_Log::DEBUG); + Minz_Log::record($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG); $feedDAO->updateLastUpdate($feed->id()); } } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index dc8a854a9..ca230232f 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -20,15 +20,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $this->catDAO->checkDefault (); } - private static function entryDateComparer($e1, $e2) { - $d1 = $e1->date(true); - $d2 = $e2->date(true); - if ($d1 === $d2) { - return 0; - } - return ($d1 < $d2) ? -1 : 1; - } - public function addAction () { @set_time_limit(300); @@ -55,7 +46,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } $feed->_httpAuth ($httpAuth); - $feed->load (); + $feed->load(true); $feedDAO = new FreshRSS_FeedDAO (); $values = array ( @@ -91,8 +82,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $is_read = $this->view->conf->markUponReception() === 'yes' ? 1 : 0; $entryDAO = new FreshRSS_EntryDAO (); - $entries = $feed->entries (); - usort($entries, 'self::entryDateComparer'); + $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order // on calcule la date des articles les plus anciens qu'on accepte $nb_month_old = $this->view->conf->oldEntries (); @@ -199,10 +189,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $flux_update = 0; foreach ($feeds as $feed) { try { - $feed->load (); - $feed->faviconPrepare(); - $entries = $feed->entries (); - usort($entries, 'self::entryDateComparer'); + $url = $feed->url(); + $feed->load(false); + $entries = array_reverse($feed->entries()); //We want chronological order and SimplePie uses reverse order $is_read = $this->view->conf->markUponReception() === 'yes' ? 1 : 0; @@ -231,7 +220,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if (($feedHistory >= 0) && (rand(0, 30) === 1)) { $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feedHistory, count($entries) + 10)); if ($nb > 0) { - Minz_Log::record ($nb . ' old entries cleaned in feed ' . $feed->id (), Minz_Log::DEBUG); + Minz_Log::record ($nb . ' old entries cleaned in feed [' . $feed->url() . ']', Minz_Log::DEBUG); } } @@ -239,6 +228,10 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO->updateLastUpdate ($feed->id ()); $feedDAO->commit (); $flux_update++; + if ($feed->url() !== $url) { //URL has changed (auto-discovery) + $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); + } + $feed->faviconPrepare(); } catch (FreshRSS_Feed_Exception $e) { Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE); $feedDAO->updateLastUpdate ($feed->id (), 1); diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 4f90b9872..0f467f776 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -16,6 +16,7 @@ class FreshRSS_Feed extends Minz_Model { private $httpAuth = ''; private $error = false; private $keep_history = -2; + private $hash = null; public function __construct ($url, $validate=true) { if ($validate) { @@ -30,7 +31,10 @@ class FreshRSS_Feed extends Minz_Model { } public function hash() { - return hash('crc32b', Minz_Configuration::salt() . $this->url); + if ($this->hash === null) { + $this->hash = hash('crc32b', Minz_Configuration::salt() . $this->url); + } + return $this->hash; } public function url () { @@ -184,7 +188,7 @@ class FreshRSS_Feed extends Minz_Model { $this->nbEntries = intval($value); } - public function load () { + public function load ($loadDetails = false) { if (!is_null ($this->url)) { if (CACHE_PATH === false) { throw new Minz_FileNotExistException ( @@ -256,11 +260,13 @@ class FreshRSS_Feed extends Minz_Model { $this->_url ($subscribe_url); } - $title = htmlspecialchars(html_only_entity_decode($feed->get_title()), ENT_COMPAT, 'UTF-8'); - $this->_name (!is_null ($title) ? $title : $this->url); + if ($loadDetails) { + $title = htmlspecialchars(html_only_entity_decode($feed->get_title()), ENT_COMPAT, 'UTF-8'); + $this->_name (!is_null ($title) ? $title : $this->url); - $this->_website(html_only_entity_decode($feed->get_link())); - $this->_description(html_only_entity_decode($feed->get_description())); + $this->_website(html_only_entity_decode($feed->get_link())); + $this->_description(html_only_entity_decode($feed->get_description())); + } // et on charge les articles du flux $this->loadEntries ($feed); -- cgit v1.2.3 From badc60910a21774512034027bbb394ffeb477629 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 27 Dec 2013 15:16:53 +0100 Subject: Petites corrections et sortie 0.7-beta3 --- app/Controllers/feedController.php | 12 ++++++------ p/i/install.php | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index ca230232f..a3371a43e 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -198,12 +198,17 @@ class FreshRSS_feed_Controller extends Minz_ActionController { //For this feed, check last n entry GUIDs already in database $existingGuids = array_fill_keys ($entryDAO->listLastGuidsByFeed ($feed->id (), count($entries) + 10), 1); + $feedHistory = $feed->keepHistory(); + if ($feedHistory == -2) { //default + $feedHistory = $this->view->conf->keepHistoryDefault(); + } + // On ne vérifie pas strictement que l'article n'est pas déjà en BDD // La BDD refusera l'ajout car (id_feed, guid) doit être unique $feedDAO->beginTransaction (); foreach ($entries as $entry) { if ((!isset ($existingGuids[$entry->guid ()])) && - ($entry->date (true) >= $date_min)) { + (($feedHistory != 0) || ($entry->date (true) >= $date_min))) { $values = $entry->toArray (); //Use declared date at first import, otherwise use discovery date $values['id'] = empty($existingGuids) ? min(time(), $entry->date (true)) . uSecString() : uTimeString(); @@ -212,11 +217,6 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - $feedHistory = $feed->keepHistory(); - if ($feedHistory == -2) { //default - $feedHistory = $this->view->conf->keepHistoryDefault(); - } - if (($feedHistory >= 0) && (rand(0, 30) === 1)) { $nb = $feedDAO->cleanOldEntries ($feed->id (), $date_min, max($feedHistory, count($entries) + 10)); if ($nb > 0) { diff --git a/p/i/install.php b/p/i/install.php index 0e8a972c6..132cd5508 100644 --- a/p/i/install.php +++ b/p/i/install.php @@ -573,7 +573,7 @@ function checkStep2 () { if ($defaultUser === null) { $defaultUser = empty($_SESSION['default_user']) ? '' : $_SESSION['default_user']; } - $data = file_exists (DATA_PATH . '/' . $defaultUser . '_user.php'); + $data = is_writable(DATA_PATH . '/' . $defaultUser . '_user.php'); if ($data) { @unlink(DATA_PATH . '/Configuration.array.php'); //v0.6 } @@ -585,7 +585,7 @@ function checkStep2 () { ); } function checkStep3 () { - $conf = file_exists (DATA_PATH . '/config.php'); + $conf = is_writable(DATA_PATH . '/config.php'); $bd = isset ($_SESSION['bd_type']) && isset ($_SESSION['bd_host']) && -- cgit v1.2.3 From a1249e56a7151ea20f64ac3091c0a3383d21049c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 27 Dec 2013 16:24:40 +0100 Subject: Petit changement utilisation de la date déclarée des articles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG | 2 +- app/Controllers/feedController.php | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'app/Controllers') diff --git a/CHANGELOG b/CHANGELOG index 9d91544c4..108e29195 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -62,7 +62,7 @@ * Utilise un nom poli “FreshRSS” (évite des problèmes avec certains filtres) * Se limite au répertoire “./FreshRSS/p/i/” pour de meilleures performances HTTP * Les images, CSS, scripts sont servis sans cookie - * utilise “HttpOnly” pour plus de sécurité + * Utilise “HttpOnly” pour plus de sécurité * Nouvel “agent utilisateur” exposé lors du téléchargement des flux, par exemple : * “FreshRSS/0.7 (Linux; http://freshrss.org) SimplePie/1.3.1” * Affichage du numéro de version dans "À propos" diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index a3371a43e..77d36e977 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -197,6 +197,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { //For this feed, check last n entry GUIDs already in database $existingGuids = array_fill_keys ($entryDAO->listLastGuidsByFeed ($feed->id (), count($entries) + 10), 1); + $useDeclaredDate = empty($existingGuids); $feedHistory = $feed->keepHistory(); if ($feedHistory == -2) { //default @@ -207,11 +208,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController { // La BDD refusera l'ajout car (id_feed, guid) doit être unique $feedDAO->beginTransaction (); foreach ($entries as $entry) { + $eDate = $entry->date (true); if ((!isset ($existingGuids[$entry->guid ()])) && - (($feedHistory != 0) || ($entry->date (true) >= $date_min))) { + (($feedHistory != 0) || ($eDate >= $date_min))) { $values = $entry->toArray (); //Use declared date at first import, otherwise use discovery date - $values['id'] = empty($existingGuids) ? min(time(), $entry->date (true)) . uSecString() : uTimeString(); + $values['id'] = ($useDeclaredDate || $eDate < $date_min) ? + min(time(), $eDate) . uSecString() : + uTimeString(); $values['is_read'] = $is_read; $entryDAO->addEntry ($values); } -- cgit v1.2.3 From 9f52ab97cb947944546917623d8fe0999d8bda9b Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 27 Dec 2013 19:23:11 +0100 Subject: Début 0.8-dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 --- README.md | 4 +- app/Controllers/configureController.php | 93 +++++++++++++++++++++++++-------- app/i18n/en.php | 10 ++-- app/i18n/fr.php | 11 ++-- app/layout/aside_configure.phtml | 13 +++-- app/layout/header.phtml | 4 +- app/views/configure/archiving.phtml | 47 +++++++++++++++++ app/views/configure/display.phtml | 73 +------------------------- app/views/configure/users.phtml | 72 +++++++++++++++++++++++++ constants.php | 2 +- 10 files changed, 217 insertions(+), 112 deletions(-) create mode 100644 app/views/configure/archiving.phtml create mode 100644 app/views/configure/users.phtml (limited to 'app/Controllers') diff --git a/README.md b/README.md index 1f1ab35c3..f20f870dd 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ Il se veut léger et facile à prendre en main tout en étant un outil puissant * Site officiel : http://freshrss.org * Démo : http://marienfressinaud.fr/projets/freshrss/ * Développeur : Marien Fressinaud -* Version actuelle : 0.7-beta3 -* Date de publication 2014-01-xx +* Version actuelle : 0.8-dev +* Date de publication 2014-xx-xx * License [GNU AGPL 3](http://www.gnu.org/licenses/agpl-3.0.html) ![Logo de FreshRSS](http://marienfressinaud.fr/data/images/freshrss/freshrss_title.png) diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 762134dd0..5fa686ccc 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -159,11 +159,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $onread_jump_next = Minz_Request::param ('onread_jump_next', 'no'); $lazyload = Minz_Request::param ('lazyload', 'no'); $sort = Minz_Request::param ('sort_order', 'DESC'); - $old = Minz_Request::param ('old_entries', 3); - $keepHistoryDefault = Minz_Request::param('keep_history_default', 0); - $mail = Minz_Request::param ('mail_login', false); - $anon = Minz_Request::param ('anon_access', 'no'); - $token = Minz_Request::param ('token', $current_token); $openArticle = Minz_Request::param ('mark_open_article', 'no'); $openSite = Minz_Request::param ('mark_open_site', 'no'); $scroll = Minz_Request::param ('mark_scroll', 'no'); @@ -189,11 +184,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_onread_jump_next ($onread_jump_next); $this->view->conf->_lazyload ($lazyload); $this->view->conf->_sortOrder ($sort); - $this->view->conf->_oldEntries ($old); - $this->view->conf->_keepHistoryDefault($keepHistoryDefault); - $this->view->conf->_mailLogin ($mail); - $this->view->conf->_anonAccess ($anon); - $this->view->conf->_token ($token); $this->view->conf->_markWhen (array ( 'article' => $openArticle, 'site' => $openSite, @@ -219,14 +209,9 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'default_view' => $this->view->conf->defaultView (), 'auto_load_more' => $this->view->conf->autoLoadMore (), 'display_posts' => $this->view->conf->displayPosts (), - 'onread_jump_next' => $this->view->conf->onread_jump_next (), + 'onread_jump_next' => $this->view->conf->onread_jump_next (), 'lazyload' => $this->view->conf->lazyload (), 'sort_order' => $this->view->conf->sortOrder (), - 'old_entries' => $this->view->conf->oldEntries (), - 'keep_history_default' => $this->view->conf->keepHistoryDefault(), - 'mail_login' => $this->view->conf->mailLogin (), - 'anon_access' => $this->view->conf->anonAccess (), - 'token' => $this->view->conf->token (), 'mark_when' => $this->view->conf->markWhen (), 'theme' => $this->view->conf->theme (), 'topline_read' => $this->view->conf->toplineRead () ? 'yes' : 'no', @@ -261,11 +246,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->themes = FreshRSS_Themes::get(); - Minz_View::prependTitle (Minz_Translate::t ('general_and_reading_management') . ' - '); - - $entryDAO = new FreshRSS_EntryDAO (); - $this->view->nb_total = $entryDAO->count (); - $this->view->size_total = $entryDAO->size (); + Minz_View::prependTitle (Minz_Translate::t ('reading_configuration') . ' - '); } public function sharingAction () { @@ -410,4 +391,74 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_View::prependTitle (Minz_Translate::t ('shortcuts_management') . ' - '); } + + public function usersAction() { + if (Minz_Request::isPost()) { + $current_token = $this->view->conf->token(); + + $mail = Minz_Request::param('mail_login', false); + $anon = Minz_Request::param('anon_access', 'no'); + $token = Minz_Request::param('token', $current_token); + $this->view->conf->_mailLogin($mail); + $this->view->conf->_anonAccess($anon); + $this->view->conf->_token($token); + + $values = array( + 'mail_login' => $this->view->conf->mailLogin(), + 'anon_access' => $this->view->conf->anonAccess(), + 'token' => $this->view->conf->token(), + ); + + $confDAO = new FreshRSS_ConfigurationDAO(); + $confDAO->update($values); + Minz_Session::_param('conf', $this->view->conf); + Minz_Session::_param('mail', $this->view->conf->mailLogin()); + + // notif + $notif = array( + 'type' => 'good', + 'content' => Minz_Translate::t('configuration_updated') + ); + Minz_Session::_param('notification', $notif); + + Minz_Request::forward(array('c' => 'configure', 'a' => 'users'), true); + } + + Minz_View::prependTitle(Minz_Translate::t ('users') . ' - '); + } + + public function archivingAction () { + if (Minz_Request::isPost()) { + $old = Minz_Request::param('old_entries', 3); + $keepHistoryDefault = Minz_Request::param('keep_history_default', 0); + + $this->view->conf->_oldEntries($old); + $this->view->conf->_keepHistoryDefault($keepHistoryDefault); + + $values = array( + 'old_entries' => $this->view->conf->oldEntries(), + 'keep_history_default' => $this->view->conf->keepHistoryDefault(), + ); + + $confDAO = new FreshRSS_ConfigurationDAO(); + $confDAO->update($values); + Minz_Session::_param('conf', $this->view->conf); + Minz_Session::_param('mail', $this->view->conf->mailLogin ()); + + // notif + $notif = array( + 'type' => 'good', + 'content' => Minz_Translate::t('configuration_updated') + ); + Minz_Session::_param('notification', $notif); + + Minz_Request::forward(array('c' => 'configure', 'a' => 'archiving'), true); + } + + Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' - '); + + $entryDAO = new FreshRSS_EntryDAO(); + $this->view->nb_total = $entryDAO->count(); + $this->view->size_total = $entryDAO->size(); + } } diff --git a/app/i18n/en.php b/app/i18n/en.php index f74a7f198..a1ab4a025 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -8,7 +8,7 @@ return array ( 'search_short' => 'Search', 'configuration' => 'Configuration', - 'general_and_reading' => 'General and reading', + 'users' => 'Users', 'categories' => 'Categories', 'category' => 'Category', 'shortcuts' => 'Shortcuts', @@ -68,7 +68,6 @@ return array ( 'feed_updated' => 'Feed has been updated', 'rss_feed_management' => 'RSS feeds management', 'configuration_updated' => 'Configuration has been updated', - 'general_and_reading_management'=> 'General and reading management', 'sharing_management' => 'Sharing options management', 'bad_opml_file' => 'Your OPML file is invalid', 'shortcuts_updated' => 'Shortcuts have been updated', @@ -157,15 +156,16 @@ return array ( 'no_selected_feed' => 'No feed selected.', 'think_to_add' => 'Think to add RSS feeds!', - 'general_configuration' => 'General configuration', - 'language' => 'Language', - 'month' => 'months', + 'current_user' => 'Current user', 'default_user' => 'Username of the default user (maximum 16 alphanumeric characters)', 'persona_connection_email' => 'Login mail address (use Mozilla Persona)', 'allow_anonymous' => 'Allow anonymous reading', 'auth_token' => 'Authentication token', 'explain_token' => 'Allows to access RSS output without authentication.
    %s?token=%s', 'login_configuration' => 'Login', + + 'language' => 'Language', + 'month' => 'months', 'archiving_configuration' => 'Archiving', 'delete_articles_every' => 'Remove articles after', 'purge_now' => 'Purge now', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index f9c4d00cc..f4e9d109c 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -8,7 +8,7 @@ return array ( 'search_short' => 'Rechercher', 'configuration' => 'Configuration', - 'general_and_reading' => 'Général et lecture', + 'users' => 'Utilisateurs', 'categories' => 'Catégories', 'category' => 'Catégorie', 'shortcuts' => 'Raccourcis', @@ -68,7 +68,6 @@ return array ( 'feed_updated' => 'Le flux a été mis à jour', 'rss_feed_management' => 'Gestion des flux RSS', 'configuration_updated' => 'La configuration a été mise à jour', - 'general_and_reading_management'=> 'Gestion générale et affichage', 'sharing_management' => 'Gestion des options de partage', 'bad_opml_file' => 'Votre fichier OPML n’est pas valide', 'shortcuts_updated' => 'Les raccourcis ont été mis à jour', @@ -157,16 +156,16 @@ return array ( 'no_selected_feed' => 'Aucun flux sélectionné.', 'think_to_add' => 'Pensez à en ajouter !', - 'general_configuration' => 'Configuration générale', - 'language' => 'Langue', - - 'month' => 'mois', + 'current_user' => 'Utilisateur actuel', 'default_user' => 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)', 'persona_connection_email' => 'Adresse courriel de connexion (utilise Mozilla Persona)', 'allow_anonymous' => 'Autoriser la lecture anonyme', 'auth_token' => 'Jeton d’identification', 'explain_token' => 'Permet d’accéder à la sortie RSS sans besoin de s’authentifier.
    %s?output=rss&token=%s', 'login_configuration' => 'Identification', + + 'language' => 'Langue', + 'month' => 'mois', 'archiving_configuration' => 'Archivage', 'delete_articles_every' => 'Supprimer les articles après', 'purge_now' => 'Purger maintenant', diff --git a/app/layout/aside_configure.phtml b/app/layout/aside_configure.phtml index aa46af95d..89f63e36c 100644 --- a/app/layout/aside_configure.phtml +++ b/app/layout/aside_configure.phtml @@ -1,8 +1,13 @@ - + diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 6cb1380a3..bba22508e 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -57,7 +57,9 @@
    - - -
    - - conf->mailLogin (); ?> -
    - - - -
    -
    - -
    - - conf->token (); ?> -
    - - -
    -
    - -
    -
    - - -
    -
    - - -

    - -
    - -
    - -   -
    -
    -
    - -
    - -
    -
    - -
    -
    - - -
    -
    -
    @@ -246,17 +187,5 @@
    - - -
    - -
    -

    nb_total; ?> , size_total); ?>.

    -

    - -

    - -
    -
    diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml new file mode 100644 index 000000000..a8e2deea1 --- /dev/null +++ b/app/views/configure/users.phtml @@ -0,0 +1,72 @@ +partial('aside_configure'); ?> + +
    + + +
    + + +
    + +
    + + $_SERVER['REMOTE_USER'] = +
    +
    + +
    +
    + +
    +
    + +
    + +
    + + (selector not implemented yet) +
    +
    + +
    +
    + + +
    +
    + + + +
    + + conf->mailLogin(); ?> +
    + + +
    +
    + +
    + + conf->token(); ?> +
    + + +
    +
    + +
    +
    + + +
    +
    +
    +
    diff --git a/constants.php b/constants.php index 0c7adc57e..a7c96f47b 100644 --- a/constants.php +++ b/constants.php @@ -1,5 +1,5 @@ Date: Fri, 27 Dec 2013 22:45:26 +0100 Subject: Redirection correcte vers nouvelle vue archivage --- app/Controllers/entryController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index 26f3422ca..9b5081894 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -106,7 +106,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { Minz_Request::forward(array( 'c' => 'configure', - 'a' => 'display' + 'a' => 'archiving' ), true); } @@ -147,7 +147,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { Minz_Request::forward(array( 'c' => 'configure', - 'a' => 'display' + 'a' => 'archiving' ), true); } } -- cgit v1.2.3 From 2063f967c63cab8bed30c8db44490a6531d4cb5d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 28 Dec 2013 00:31:52 +0100 Subject: Création de lib_opml pour alléger lib_rss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Et suppression de fonctions inutilisées --- app/Controllers/configureController.php | 1 + app/views/configure/importExport.phtml | 7 +- lib/lib_opml.php | 121 +++++++++++++++++++++++++++++ lib/lib_rss.php | 133 -------------------------------- 4 files changed, 127 insertions(+), 135 deletions(-) create mode 100644 lib/lib_opml.php (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 5fa686ccc..487f6e4ad 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -283,6 +283,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { } public function importExportAction () { + require_once(LIB_PATH . '/lib_opml.php'); $catDAO = new FreshRSS_CategoryDAO (); $this->view->categories = $catDAO->listCategories (); diff --git a/app/views/configure/importExport.phtml b/app/views/configure/importExport.phtml index 29a0a682b..e2217d9ed 100644 --- a/app/views/configure/importExport.phtml +++ b/app/views/configure/importExport.phtml @@ -1,5 +1,8 @@ -req == 'export') { ?> -'; // résout bug sur certain serveur ?> +req == 'export') { + echo ''; +?> diff --git a/lib/lib_opml.php b/lib/lib_opml.php new file mode 100644 index 000000000..1b5517d7f --- /dev/null +++ b/lib/lib_opml.php @@ -0,0 +1,121 @@ +' . "\n"; + + foreach ($cat['feeds'] as $feed) { + $txt .= "\t" . '' . "\n"; + } + + $txt .= '' . "\n"; + } + + return $txt; +} + +function opml_import ($xml) { + $xml = html_only_entity_decode($xml); //!\ Assume UTF-8 + + $dom = new DOMDocument(); + $dom->recover = true; + $dom->strictErrorChecking = false; + $dom->loadXML($xml); + $dom->encoding = 'UTF-8'; + + $opml = simplexml_import_dom($dom); + + if (!$opml) { + throw new FreshRSS_Opml_Exception (); + } + + $catDAO = new FreshRSS_CategoryDAO(); + $catDAO->checkDefault(); + $defCat = $catDAO->getDefault(); + + $categories = array (); + $feeds = array (); + + foreach ($opml->body->outline as $outline) { + if (!isset ($outline['xmlUrl'])) { + // Catégorie + $title = ''; + + if (isset ($outline['text'])) { + $title = (string) $outline['text']; + } elseif (isset ($outline['title'])) { + $title = (string) $outline['title']; + } + + if ($title) { + // Permet d'éviter les soucis au niveau des id : + // ceux-ci sont générés en fonction de la date, + // un flux pourrait être dans une catégorie X avec l'id Y + // alors qu'il existe déjà la catégorie X mais avec l'id Z + // Y ne sera pas ajouté et le flux non plus vu que l'id + // de sa catégorie n'exisera pas + $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); + $catDAO = new FreshRSS_CategoryDAO (); + $cat = $catDAO->searchByName ($title); + if ($cat === false) { + $cat = new FreshRSS_Category ($title); + $values = array ( + 'name' => $cat->name (), + 'color' => $cat->color () + ); + $cat->_id ($catDAO->addCategory ($values)); + } + + $feeds = array_merge ($feeds, getFeedsOutline ($outline, $cat->id ())); + } + } else { + // Flux rss sans catégorie, on récupère l'ajoute dans la catégorie par défaut + $feeds[] = getFeed ($outline, $defCat->id()); + } + } + + return array ($categories, $feeds); +} + +/** + * import all feeds of a given outline tag + */ +function getFeedsOutline ($outline, $cat_id) { + $feeds = array (); + + foreach ($outline->children () as $child) { + if (isset ($child['xmlUrl'])) { + $feeds[] = getFeed ($child, $cat_id); + } else { + $feeds = array_merge( + $feeds, + getFeedsOutline ($child, $cat_id) + ); + } + } + + return $feeds; +} + +function getFeed ($outline, $cat_id) { + $url = (string) $outline['xmlUrl']; + $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); + $title = ''; + if (isset ($outline['text'])) { + $title = (string) $outline['text']; + } elseif (isset ($outline['title'])) { + $title = (string) $outline['title']; + } + $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); + $feed = new FreshRSS_Feed ($url); + $feed->_category ($cat_id); + $feed->_name ($title); + if (isset($outline['htmlUrl'])) { + $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_COMPAT, 'UTF-8')); + } + if (isset($outline['description'])) { + $feed->_description(sanitizeHTML((string)$outline['description'])); + } + return $feed; +} diff --git a/lib/lib_rss.php b/lib/lib_rss.php index a1fadcb24..160a65bbf 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -98,33 +98,6 @@ function timestamptodate ($t, $hour = true) { return @date ($date, $t); } -function sortEntriesByDate ($entry1, $entry2) { - return $entry2->date (true) - $entry1->date (true); -} -function sortReverseEntriesByDate ($entry1, $entry2) { - return $entry1->date (true) - $entry2->date (true); -} - -function get_domain ($url) { - return parse_url($url, PHP_URL_HOST); -} - -function opml_export ($cats) { - $txt = ''; - - foreach ($cats as $cat) { - $txt .= '' . "\n"; - - foreach ($cat['feeds'] as $feed) { - $txt .= "\t" . '' . "\n"; - } - - $txt .= '' . "\n"; - } - - return $txt; -} - function html_only_entity_decode($text) { static $htmlEntitiesOnly = null; if ($htmlEntitiesOnly === null) { @@ -144,112 +117,6 @@ function sanitizeHTML($data) { return html_only_entity_decode($simplePie->sanitize->sanitize($data, SIMPLEPIE_CONSTRUCT_MAYBE_HTML)); } -function opml_import ($xml) { - $xml = html_only_entity_decode($xml); //!\ Assume UTF-8 - - $dom = new DOMDocument(); - $dom->recover = true; - $dom->strictErrorChecking = false; - $dom->loadXML($xml); - $dom->encoding = 'UTF-8'; - - $opml = simplexml_import_dom($dom); - - if (!$opml) { - throw new FreshRSS_Opml_Exception (); - } - - $catDAO = new FreshRSS_CategoryDAO(); - $catDAO->checkDefault(); - $defCat = $catDAO->getDefault(); - - $categories = array (); - $feeds = array (); - - foreach ($opml->body->outline as $outline) { - if (!isset ($outline['xmlUrl'])) { - // Catégorie - $title = ''; - - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - - if ($title) { - // Permet d'éviter les soucis au niveau des id : - // ceux-ci sont générés en fonction de la date, - // un flux pourrait être dans une catégorie X avec l'id Y - // alors qu'il existe déjà la catégorie X mais avec l'id Z - // Y ne sera pas ajouté et le flux non plus vu que l'id - // de sa catégorie n'exisera pas - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $catDAO = new FreshRSS_CategoryDAO (); - $cat = $catDAO->searchByName ($title); - if ($cat === false) { - $cat = new FreshRSS_Category ($title); - $values = array ( - 'name' => $cat->name (), - 'color' => $cat->color () - ); - $cat->_id ($catDAO->addCategory ($values)); - } - - $feeds = array_merge ($feeds, getFeedsOutline ($outline, $cat->id ())); - } - } else { - // Flux rss sans catégorie, on récupère l'ajoute dans la catégorie par défaut - $feeds[] = getFeed ($outline, $defCat->id()); - } - } - - return array ($categories, $feeds); -} - -/** - * import all feeds of a given outline tag - */ -function getFeedsOutline ($outline, $cat_id) { - $feeds = array (); - - foreach ($outline->children () as $child) { - if (isset ($child['xmlUrl'])) { - $feeds[] = getFeed ($child, $cat_id); - } else { - $feeds = array_merge( - $feeds, - getFeedsOutline ($child, $cat_id) - ); - } - } - - return $feeds; -} - -function getFeed ($outline, $cat_id) { - $url = (string) $outline['xmlUrl']; - $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); - $title = ''; - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $feed = new FreshRSS_Feed ($url); - $feed->_category ($cat_id); - $feed->_name ($title); - if (isset($outline['htmlUrl'])) { - $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_COMPAT, 'UTF-8')); - } - if (isset($outline['description'])) { - $feed->_description(sanitizeHTML((string)$outline['description'])); - } - return $feed; -} - - /* permet de récupérer le contenu d'un article pour un flux qui n'est pas complet */ function get_content_by_parsing ($url, $path) { require_once (LIB_PATH . '/lib_phpQuery.php'); -- cgit v1.2.3 From 9ac1496d63da32524a33696187342ce061e9ef28 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 28 Dec 2013 13:54:52 +0100 Subject: Bouge anon_access dans config.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'accès anonyme n'est pas au niveau utilisateur mais au niveau global. Bouge FreshRSS_Configuration::conf->anonAccess() qui était stocké dans *_user.php vers Minz_Configuration::allowAnonymous() stocké dans config.php Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 + autres optimisations Contribue à https://github.com/marienfressinaud/FreshRSS/issues/260 --- app/Controllers/configureController.php | 11 ++++--- app/Controllers/indexController.php | 6 ++-- app/Models/Configuration.php | 12 ------- app/Models/ConfigurationDAO.php | 4 --- app/layout/header.phtml | 2 +- app/views/configure/users.phtml | 2 +- app/views/index/index.phtml | 2 +- data/.gitignore | 3 +- lib/Minz/Configuration.php | 58 ++++++++++++++++++++++++++------- 9 files changed, 61 insertions(+), 39 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 487f6e4ad..dd9674588 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -398,15 +398,13 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $current_token = $this->view->conf->token(); $mail = Minz_Request::param('mail_login', false); - $anon = Minz_Request::param('anon_access', 'no'); $token = Minz_Request::param('token', $current_token); + $this->view->conf->_mailLogin($mail); - $this->view->conf->_anonAccess($anon); $this->view->conf->_token($token); $values = array( 'mail_login' => $this->view->conf->mailLogin(), - 'anon_access' => $this->view->conf->anonAccess(), 'token' => $this->view->conf->token(), ); @@ -415,7 +413,12 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Session::_param('conf', $this->view->conf); Minz_Session::_param('mail', $this->view->conf->mailLogin()); - // notif + $anon = (bool)(Minz_Request::param('anon_access', false)); + if ($anon != Minz_Configuration::allowAnonymous()) { + Minz_Configuration::_allowAnonymous($anon); + Minz_Configuration::writeFile(); + } + $notif = array( 'type' => 'good', 'content' => Minz_Translate::t('configuration_updated') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 6c0ba9058..0c229aedb 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -24,7 +24,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { // check if user is log in if(login_is_conf ($this->view->conf) && !is_logged() && - $this->view->conf->anonAccess() === 'no' && + !Minz_Configuration::allowAnonymous() && !($output === 'rss' && $token_is_ok)) { return; } @@ -36,8 +36,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { $params['search'] = urlencode ($params['search']); } if (login_is_conf($this->view->conf) && - $this->view->conf->anonAccess() === 'no' && - $token != '') { + !Minz_Configuration::allowAnonymous() && + $token !== '') { $params['token'] = $token; } $this->view->rss_url = array ( diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index cb2f90655..7f4be474d 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -20,7 +20,6 @@ class FreshRSS_Configuration extends Minz_Model { private $mark_when = array (); private $sharing = array (); private $theme; - private $anon_access; private $token; private $auto_load_more; private $topline_read; @@ -52,7 +51,6 @@ class FreshRSS_Configuration extends Minz_Model { $this->_sharing ($confDAO->sharing); $this->_theme ($confDAO->theme); FreshRSS_Themes::setThemeId ($confDAO->theme); - $this->_anonAccess ($confDAO->anon_access); $this->_token ($confDAO->token); $this->_autoLoadMore ($confDAO->auto_load_more); $this->_topline_read ($confDAO->topline_read); @@ -132,9 +130,6 @@ class FreshRSS_Configuration extends Minz_Model { public function theme () { return $this->theme; } - public function anonAccess () { - return $this->anon_access; - } public function token () { return $this->token; } @@ -283,13 +278,6 @@ class FreshRSS_Configuration extends Minz_Model { public function _theme ($value) { $this->theme = $value; } - public function _anonAccess ($value) { - if ($value == 'yes') { - $this->anon_access = 'yes'; - } else { - $this->anon_access = 'no'; - } - } public function _token ($value) { $this->token = $value; } diff --git a/app/Models/ConfigurationDAO.php b/app/Models/ConfigurationDAO.php index 91210e701..fa4d3338f 100644 --- a/app/Models/ConfigurationDAO.php +++ b/app/Models/ConfigurationDAO.php @@ -38,7 +38,6 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { 'print' => true ); public $theme = 'default'; - public $anon_access = 'no'; public $token = ''; public $auto_load_more = 'yes'; public $topline_read = 'yes'; @@ -108,9 +107,6 @@ class FreshRSS_ConfigurationDAO extends Minz_ModelArray { if (isset ($this->array['theme'])) { $this->theme = $this->array['theme']; } - if (isset ($this->array['anon_access'])) { - $this->anon_access = $this->array['anon_access']; - } if (isset ($this->array['token'])) { $this->token = $this->array['token']; } diff --git a/app/layout/header.phtml b/app/layout/header.phtml index bba22508e..aeb417a6e 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -21,7 +21,7 @@ @@ -61,16 +61,16 @@
    @@ -79,9 +79,9 @@
    @@ -89,9 +89,9 @@
    @@ -99,9 +99,9 @@
    @@ -110,19 +110,19 @@
    @@ -132,7 +132,7 @@
    @@ -162,20 +162,20 @@ - conf->toplineRead () ? ' checked="checked"' : ''; ?> /> - conf->toplineFavorite () ? ' checked="checked"' : ''; ?> /> + conf->topline_read ? ' checked="checked"' : ''; ?> /> + conf->topline_favorite ? ' checked="checked"' : ''; ?> /> - conf->toplineDate () ? ' checked="checked"' : ''; ?> /> - conf->toplineLink () ? ' checked="checked"' : ''; ?> /> + conf->topline_date ? ' checked="checked"' : ''; ?> /> + conf->topline_link ? ' checked="checked"' : ''; ?> /> - conf->bottomlineRead () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineFavorite () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineSharing () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineTags () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineDate () ? ' checked="checked"' : ''; ?> /> - conf->bottomlineLink () ? ' checked="checked"' : ''; ?> /> + conf->bottomline_read ? ' checked="checked"' : ''; ?> /> + conf->bottomline_favorite ? ' checked="checked"' : ''; ?> /> + conf->bottomline_sharing ? ' checked="checked"' : ''; ?> /> + conf->bottomline_tags ? ' checked="checked"' : ''; ?> /> + conf->bottomline_date ? ' checked="checked"' : ''; ?> /> + conf->bottomline_link ? ' checked="checked"' : ''; ?> />
    diff --git a/app/views/configure/sharing.phtml b/app/views/configure/sharing.phtml index 825537fc9..c6a96b48a 100644 --- a/app/views/configure/sharing.phtml +++ b/app/views/configure/sharing.phtml @@ -47,7 +47,7 @@ foreach ($services as $service) { ?> diff --git a/app/views/configure/shortcut.phtml b/app/views/configure/shortcut.phtml index e78d91820..2e564a7b6 100644 --- a/app/views/configure/shortcut.phtml +++ b/app/views/configure/shortcut.phtml @@ -9,7 +9,7 @@ - conf->shortcuts (); ?> + conf->shortcuts; ?> diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index c57671ef3..4fd291ba3 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -20,7 +20,7 @@
    - conf->mailLogin(); ?> + conf->mail_login; ?>
    @@ -29,7 +29,7 @@
    - conf->token(); ?> + conf->token; ?>
    @@ -51,7 +51,7 @@
    diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index d008e2e48..8f508487c 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -1,16 +1,16 @@ conf->markWhen (); + $mark = $this->conf->mark_when; echo 'var ', - 'hide_posts=', ($this->conf->displayPosts () === 'yes' || Minz_Request::param ('output') === 'reader') ? 'false' : 'true', - ',auto_mark_article=', $mark['article'] === 'yes' ? 'true' : 'false', - ',auto_mark_site=', $mark['site'] === 'yes' ? 'true' : 'false', - ',auto_mark_scroll=', $mark['scroll'] === 'yes' ? 'true' : 'false', - ',auto_load_more=', $this->conf->autoLoadMore () === 'yes' ? 'true' : 'false', - ',full_lazyload=', $this->conf->lazyload () === 'yes' && ($this->conf->displayPosts () === 'yes' || Minz_Request::param ('output') === 'reader') ? 'true' : 'false', - ',does_lazyload=', $this->conf->lazyload() === 'yes' ? 'true' : 'false'; + 'hide_posts=', ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'false' : 'true', + ',auto_mark_article=', $mark['article'] ? 'true' : 'false', + ',auto_mark_site=', $mark['site'] ? 'true' : 'false', + ',auto_mark_scroll=', $mark['scroll'] ? 'true' : 'false', + ',auto_load_more=', $this->conf->auto_load_more ? 'true' : 'false', + ',full_lazyload=', $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param('output') === 'reader') ? 'true' : 'false', + ',does_lazyload=', $this->conf->lazyload ? 'true' : 'false'; - $s = $this->conf->shortcuts (); + $s = $this->conf->shortcuts; echo ',shortcuts={', 'mark_read:"', $s['mark_read'], '",', 'mark_favorite:"', $s['mark_favorite'], '",', diff --git a/app/views/helpers/view/global_view.phtml b/app/views/helpers/view/global_view.phtml index bc6e24e37..58ff13d4e 100644 --- a/app/views/helpers/view/global_view.phtml +++ b/app/views/helpers/view/global_view.phtml @@ -31,6 +31,6 @@
    -
    conf->displayPosts () === 'no' ? ' class="hide_posts"' : ''; ?>> +
    conf->display_posts ? '' : ' class="hide_posts"'; ?>> -
    \ No newline at end of file +
    diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 4307c2113..f59cae2b8 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -18,8 +18,8 @@ if (!empty($this->entries)) { $email = $this->conf->sharing ('email'); $print = $this->conf->sharing ('print'); $today = $this->today; - $hidePosts = $this->conf->displayPosts() === 'no'; - $lazyload = $this->conf->lazyload() === 'yes'; + $hidePosts = !$this->conf->display_posts; + $lazyload = $this->conf->lazyload; ?>
    @@ -49,13 +49,13 @@ if (!empty($this->entries)) {
    @@ -86,13 +86,13 @@ if (!empty($this->entries)) {
      conf->bottomlineRead ()) { + if ($this->conf->bottomline_read) { ?>
    • isRead () ? 'read' : 'unread'); ?>
    • conf->bottomlineFavorite ()) { + if ($this->conf->bottomline_favorite) { ?>
    • isFavorite () ? 'starred' : 'non-starred'); ?>entries)) { } ?>
    • conf->bottomlineSharing () && ( + if ($this->conf->bottomline_sharing && ( $shaarli || $poche || $diaspora || $twitter || $google_plus || $facebook || $email )) { @@ -171,7 +171,7 @@ if (!empty($this->entries)) {
    • conf->bottomlineTags () ? $item->tags() : null; + $tags = $this->conf->bottomline_tags ? $item->tags() : null; if (!empty($tags)) { ?>
    • @@ -190,8 +190,8 @@ if (!empty($this->entries)) {
    - conf->bottomlineDate ()) { ?>
  • date (); ?> 
  • - conf->bottomlineLink ()) { ?> + conf->bottomline_date) { ?>
  • date (); ?> 
  • + conf->bottomline_link) { ?>
    diff --git a/app/views/helpers/view/reader_view.phtml b/app/views/helpers/view/reader_view.phtml index 47254f74e..2f64e672a 100644 --- a/app/views/helpers/view/reader_view.phtml +++ b/app/views/helpers/view/reader_view.phtml @@ -2,7 +2,7 @@ $this->partial ('nav_menu'); if (!empty($this->entries)) { - $lazyload = $this->conf->lazyload() === 'yes'; + $lazyload = $this->conf->lazyload; ?>
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 2d134ba4e..4db53e2a5 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -1,7 +1,7 @@ conf->token(); +$token = $this->conf->token; $token_param = Minz_Request::param ('token', ''); $token_is_ok = ($token != '' && $token == $token_param); diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 306328904..3864a9335 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -225,14 +225,14 @@ class Minz_Configuration { } } if (isset ($general['delay_cache'])) { - self::$delay_cache = $general['delay_cache']; + self::$delay_cache = inval($general['delay_cache']); } if (isset ($general['default_user'])) { self::$default_user = $general['default_user']; self::$current_user = self::$default_user; } if (isset ($general['allow_anonymous'])) { - self::$allow_anonymous = (bool)($general['allow_anonymous']); + self::$allow_anonymous = ((bool)($general['allow_anonymous'])) && ($general['allow_anonymous'] !== 'no'); } // Base de données diff --git a/lib/Minz/ModelArray.php b/lib/Minz/ModelArray.php index 89d7f06c1..e3ec77dc9 100644 --- a/lib/Minz/ModelArray.php +++ b/lib/Minz/ModelArray.php @@ -8,11 +8,6 @@ * La classe Model_array représente le modèle interragissant avec les fichiers de type texte gérant des tableaux php */ class Minz_ModelArray { - /** - * $array Le tableau php contenu dans le fichier $filename - */ - protected $array = array (); - /** * $filename est le nom du fichier */ @@ -25,29 +20,32 @@ class Minz_ModelArray { */ public function __construct ($filename) { $this->filename = $filename; + } + protected function loadArray() { if (!file_exists($this->filename)) { throw new Minz_FileNotExistException($this->filename, Minz_Exception::WARNING); } elseif (($handle = $this->getLock()) === false) { throw new Minz_PermissionDeniedException($this->filename); } else { - $this->array = include($this->filename); + $data = include($this->filename); $this->releaseLock($handle); - if ($this->array === false) { + if ($data === false) { throw new Minz_PermissionDeniedException($this->filename); - } elseif (!is_array($this->array)) { - $this->array = array(); + } elseif (!is_array($data)) { + $data = array(); } + return $data; } } /** * Sauve le tableau $array dans le fichier $filename **/ - protected function writeFile() { - if (!file_put_contents($this->filename, "array, true) . ';', LOCK_EX)) { + protected function writeArray($array) { + if (!file_put_contents($this->filename, "filename); } return true; diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index f527322f5..6e45fd226 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -55,11 +55,6 @@ class Minz_Session { } else { $_SESSION[$p] = $v; self::$session[$p] = $v; - - if($p == 'language') { - // reset pour remettre à jour le fichier de langue à utiliser - Minz_Translate::reset (); - } } } @@ -76,6 +71,7 @@ class Minz_Session { if (!$force) { self::_param ('language', $language); + Minz_Translate::reset (); } } } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index a27ef171a..3f55c7d58 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -63,7 +63,7 @@ function is_logged () { // vérifie que le système d'authentification est configuré function login_is_conf ($conf) { - return $conf->mailLogin () != false; + return $conf->mail_login != ''; } // tiré de Shaarli de Seb Sauvage //Format RFC 4648 base64url -- cgit v1.2.3 From 220341b40642771f9b5db97296edfb1913182464 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 29 Dec 2013 02:12:46 +0100 Subject: Implémente sélecteur de méthode d’authentification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 --- app/Controllers/configureController.php | 7 +++++-- app/views/configure/users.phtml | 7 +++---- lib/Minz/Configuration.php | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index aabc3e4af..0c0b4951d 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -326,10 +326,13 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Session::_param('mail', $this->view->conf->mail_login); if (Minz_Configuration::isAdmin()) { - $anon = (Minz_Request::param('anon_access', false)); + $anon = Minz_Request::param('anon_access', false); $anon = ((bool)$anon) && ($anon !== 'no'); - if ($anon != Minz_Configuration::allowAnonymous()) { + $auth_type = Minz_Request::param('auth_type', 'none'); + if ($anon != Minz_Configuration::allowAnonymous() || + $auth_type != Minz_Configuration::authType()) { Minz_Configuration::_allowAnonymous($anon); + Minz_Configuration::_authType($auth_type); $ok &= Minz_Configuration::writeFile(); } } diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 4fd291ba3..7e8edf9af 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -60,11 +60,10 @@
    - (selector not implemented yet)
    diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 3864a9335..d0c530ef7 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -53,6 +53,7 @@ class Minz_Configuration { private static $default_user = ''; private static $current_user = ''; private static $allow_anonymous = false; + private static $auth_type = 'none'; private static $db = array ( 'host' => false, @@ -103,9 +104,23 @@ class Minz_Configuration { public static function allowAnonymous() { return self::$allow_anonymous; } + public static function authType() { + return self::$auth_type; + } + public static function _allowAnonymous($allow = false) { self::$allow_anonymous = (bool)$allow; } + public static function _authType($value) { + $value = strtolower($value); + switch ($value) { + case 'none': + case 'http_auth': + case 'persona': + self::$auth_type = $value; + break; + } + } /** * Initialise les variables de configuration @@ -133,6 +148,7 @@ class Minz_Configuration { 'title' => self::$title, 'default_user' => self::$default_user, 'allow_anonymous' => self::$allow_anonymous, + 'auth_type' => self::$auth_type, ), 'db' => self::$db, ); @@ -234,6 +250,9 @@ class Minz_Configuration { if (isset ($general['allow_anonymous'])) { self::$allow_anonymous = ((bool)($general['allow_anonymous'])) && ($general['allow_anonymous'] !== 'no'); } + if (isset ($general['auth_type'])) { + self::_authType($general['auth_type']); + } // Base de données $db = false; -- cgit v1.2.3 From 92efd68a3a13e49fe7bbfb8441611c0dcd639415 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 30 Dec 2013 01:03:32 +0100 Subject: Début de mode multi-utilisateur avec http_auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + Légère optimisation de Minz_View. + Encore plus de tests de bibliothèques dans install.php Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 et https://github.com/marienfressinaud/FreshRSS/issues/303 --- README.md | 2 +- app/Controllers/configureController.php | 5 +-- app/Controllers/entryController.php | 4 +-- app/Controllers/feedController.php | 27 +++++++------- app/Controllers/indexController.php | 31 ++++++++--------- app/FreshRSS.php | 56 ++++++++++++++++++++++------- app/Models/Configuration.php | 15 ++++---- app/actualize_script.php | 15 +++++--- app/i18n/en.php | 5 +-- app/i18n/fr.php | 5 +-- app/layout/aside_flux.phtml | 6 ++-- app/layout/header.phtml | 21 +++++------ app/layout/nav_menu.phtml | 2 +- app/views/configure/users.phtml | 51 +++++++++++++++------------ app/views/helpers/javascript_vars.phtml | 2 +- app/views/helpers/view/normal_view.phtml | 60 ++++++++++++++++++-------------- app/views/index/index.phtml | 45 ++++++++++++++---------- lib/Minz/Configuration.php | 12 ++++++- lib/Minz/View.php | 29 ++++++--------- lib/lib_rss.php | 16 +++------ p/i/install.php | 24 ++++++++++--- 21 files changed, 246 insertions(+), 187 deletions(-) (limited to 'app/Controllers') diff --git a/README.md b/README.md index f20f870dd..cfef89781 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Privilégiez pour cela des demandes sur GitHub # Pré-requis * Serveur Apache2 ou Nginx (non testé sur les autres) * PHP 5.2+ (PHP 5.3.3+ recommandé) - * Requis : [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [cURL](http://php.net/curl), [PDO_MySQL](http://php.net/pdo-mysql) + * Requis : [PDO_MySQL](http://php.net/pdo-mysql), [cURL](http://php.net/curl), [LibXML](http://php.net/xml), [PCRE](http://php.net/pcre), [ctype](http://php.net/ctype) * Recommandés : [JSON](http://php.net/json), [zlib](http://php.net/zlib), [mbstring](http://php.net/mbstring), [iconv](http://php.net/iconv) * MySQL 5.0.3+ (ou SQLite 3.7.4+ à venir) * Un navigateur Web récent tel Firefox, Chrome, Opera, Safari, Internet Explorer 9+ diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 0c0b4951d..656e2ac89 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -2,7 +2,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { public function firstAction () { - if (login_is_conf ($this->view->conf) && !is_logged ()) { + if (!$this->view->loginOk) { Minz_Error::error ( 403, array ('error' => array (Minz_Translate::t ('access_denied'))) @@ -16,7 +16,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController { public function categorizeAction () { $feedDAO = new FreshRSS_FeedDAO (); $catDAO = new FreshRSS_CategoryDAO (); - $catDAO->checkDefault (); $defaultCategory = $catDAO->getDefault (); $defaultId = $defaultCategory->id (); @@ -167,8 +166,6 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_bottomline_link(Minz_Request::param('bottomline_link', false)); $this->view->conf->save(); - Minz_Session::_param ('mail', $this->view->conf->mail_login); - Minz_Session::_param ('language', $this->view->conf->language); Minz_Translate::reset (); diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index b0fc37cdf..da4ab5ecc 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -2,7 +2,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { public function firstAction () { - if (login_is_conf ($this->view->conf) && !is_logged ()) { + if (!$this->view->loginOk) { Minz_Error::error ( 403, array ('error' => array (Minz_Translate::t ('access_denied'))) @@ -38,7 +38,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $nextGet = Minz_Request::param ('nextGet', $get); $idMax = Minz_Request::param ('idMax', 0); - $is_read = !!$is_read; + $is_read = (bool)$is_read; $entryDAO = new FreshRSS_EntryDAO (); if ($id == false) { diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 42a0dcb11..2d7c0ab43 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -2,18 +2,17 @@ class FreshRSS_feed_Controller extends Minz_ActionController { public function firstAction () { - $token = $this->view->conf->token; - $token_param = Minz_Request::param ('token', ''); - $token_is_ok = ($token != '' && $token == $token_param); - $action = Minz_Request::actionName (); - - if (login_is_conf ($this->view->conf) && - !is_logged () && - !($token_is_ok && $action == 'actualize')) { - Minz_Error::error ( - 403, - array ('error' => array (Minz_Translate::t ('access_denied'))) - ); + if (!$this->view->loginOk) { + $token = $this->view->conf->token; //TODO: check the token logic again, and if it is still needed + $token_param = Minz_Request::param ('token', ''); + $token_is_ok = ($token != '' && $token == $token_param); + $action = Minz_Request::actionName (); + if (!($token_is_ok && $action === 'actualize')) { + Minz_Error::error ( + 403, + array ('error' => array (Minz_Translate::t ('access_denied'))) + ); + } } $this->catDAO = new FreshRSS_CategoryDAO (); @@ -411,10 +410,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } private function addCategories ($categories) { - $catDAO = new FreshRSS_CategoryDAO (); - foreach ($categories as $cat) { - if (!$catDAO->searchByName ($cat->name ())) { + if (!$this->catDAO->searchByName ($cat->name ())) { $values = array ( 'id' => $cat->id (), 'name' => $cat->name (), diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 54826636f..66809964d 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -16,17 +16,18 @@ class FreshRSS_index_Controller extends Minz_ActionController { public function indexAction () { $output = Minz_Request::param ('output'); - - $token = $this->view->conf->token; - $token_param = Minz_Request::param ('token', ''); - $token_is_ok = ($token != '' && $token === $token_param); - - // check if user is log in - if(login_is_conf ($this->view->conf) && - !is_logged() && - !Minz_Configuration::allowAnonymous() && - !($output === 'rss' && $token_is_ok)) { - return; + $token = ''; + + // check if user is logged in + if (!$this->view->loginOk && !Minz_Configuration::allowAnonymous()) + { + $token = $this->view->conf->token; + $token_param = Minz_Request::param ('token', ''); + $token_is_ok = ($token != '' && $token === $token_param); + if (!($output === 'rss' && $token_is_ok)) { + return; + } + $params['token'] = $token; } // construction of RSS url of this feed @@ -35,11 +36,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { if (isset ($params['search'])) { $params['search'] = urlencode ($params['search']); } - if (login_is_conf($this->view->conf) && - !Minz_Configuration::allowAnonymous() && - $token !== '') { - $params['token'] = $token; - } $this->view->rss_url = array ( 'c' => 'index', 'a' => 'index', @@ -212,7 +208,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function logsAction () { - if (login_is_conf ($this->view->conf) && !is_logged ()) { + if (!$this->view->loginOk) { Minz_Error::error ( 403, array ('error' => array (Minz_Translate::t ('access_denied'))) @@ -255,6 +251,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $res = json_decode ($result, true); if ($res['status'] === 'okay' && $res['email'] === $this->view->conf->mail_login) { Minz_Session::_param ('mail', $res['email']); + $this->view->loginOk = true; invalidateHttpCache(); } else { $res = array (); diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 05c8ec8e0..10f362717 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -1,26 +1,56 @@ loadParamsView (); - $this->loadStylesAndScripts (); - $this->loadNotifications (); + public function init($currentUser = null) { + Minz_Session::init('FreshRSS'); + $this->accessControl($currentUser); + $this->loadParamsView(); + $this->loadStylesAndScripts(); + $this->loadNotifications(); } - private function loadParamsView () { + private function accessControl($currentUser) { + if ($currentUser === null) { + switch (Minz_Configuration::authType()) { + case 'http_auth': + $currentUser = httpAuthUser(); + $loginOk = $currentUser != ''; + break; + case 'persona': + $currentUser = Minz_Configuration::defaultUser(); + $loginOk = Minz_Session::param('mail') != ''; + break; + case 'none': + $currentUser = Minz_Configuration::defaultUser(); + $loginOk = true; + break; + default: + $loginOk = false; + break; + } + } elseif ((PHP_SAPI === 'cli') && (Minz_Request::actionName() === 'actualize')) { //Command line + Minz_Configuration::_authType('none'); + $loginOk = true; + } + + if (!$loginOk || !isValidUser($currentUser)) { + $currentUser = Minz_Configuration::defaultUser(); + $loginOk = false; + } + Minz_Configuration::_currentUser($currentUser); + Minz_View::_param ('loginOk', $loginOk); + try { - $this->conf = new FreshRSS_Configuration(); + $this->conf = new FreshRSS_Configuration($currentUser); } catch (Minz_Exception $e) { // Permission denied or conf file does not exist - // it's critical! die($e->getMessage()); } - Minz_View::_param ('conf', $this->conf); + } + + private function loadParamsView () { Minz_Session::_param ('language', $this->conf->language); Minz_Translate::init(); - $output = Minz_Request::param ('output'); if (!$output) { $output = $this->conf->view_mode; @@ -31,12 +61,12 @@ class FreshRSS extends Minz_FrontController { private function loadStylesAndScripts () { $theme = FreshRSS_Themes::get_infos($this->conf->theme); if ($theme) { - foreach($theme["files"] as $file) { + foreach($theme['files'] as $file) { Minz_View::appendStyle (Minz_Url::display ('/themes/' . $theme['path'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['path'] . '/' . $file))); } } - if (login_is_conf ($this->conf)) { + if (Minz_Configuration::authType() === 'persona') { Minz_View::appendScript ('https://login.persona.org/include.js'); } $includeLazyLoad = $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param ('output') === 'reader'); diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index b0a5d9940..ec7daaa7d 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -59,10 +59,9 @@ class FreshRSS_Configuration extends Minz_ModelArray { 'fr' => 'Français', ); - public function __construct ($filename = '') { - if (empty($filename)) { - $filename = DATA_PATH . '/' . Minz_Configuration::currentUser () . '_user.php'; - } + public function __construct ($user) { + $filename = DATA_PATH . '/' . $user . '_user.php'; + parent::__construct($filename); $data = parent::loadArray(); @@ -72,6 +71,7 @@ class FreshRSS_Configuration extends Minz_ModelArray { $this->$function($value); } } + $this->data['user'] = $user; } public function save() { @@ -151,10 +151,11 @@ class FreshRSS_Configuration extends Minz_ModelArray { } } public function _mail_login ($value) { - if (filter_var($value, FILTER_VALIDATE_EMAIL)) { - $this->mail_login = $value; + $value = filter_var($value, FILTER_VALIDATE_EMAIL); + if ($value) { + $this->data['mail_login'] = $value; } else { - $this->mail_login = ''; + $this->data['mail_login'] = ''; } } public function _anon_access ($value) { diff --git a/app/actualize_script.php b/app/actualize_script.php index 20438128a..e0c560ff7 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -1,6 +1,8 @@ init (); -Minz_Session::_param('mail', true); // permet de se passer de la phase de connexion -$front_controller->run (); -invalidateHttpCache(); + +$users = listUsers(); +shuffle($users); + +foreach ($users as $user) { + $front_controller->init($user); + $front_controller->run(); + invalidateHttpCache($user); +} diff --git a/app/i18n/en.php b/app/i18n/en.php index 65afc11e5..8b9eee548 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -158,13 +158,14 @@ return array ( 'current_user' => 'Current user', 'default_user' => 'Username of the default user (maximum 16 alphanumeric characters)', - 'persona_connection_email' => 'Login mail address (use Mozilla Persona)', + 'persona_connection_email' => 'Login mail address (for Mozilla Persona)', 'allow_anonymous' => 'Allow anonymous reading for the default user (%s)', 'auth_token' => 'Authentication token', - 'explain_token' => 'Allows to access RSS output without authentication.
    %s?token=%s', + 'explain_token' => 'Allows to access RSS output of the default user without authentication.
    %s?token=%s', 'login_configuration' => 'Login', 'is_admin' => 'is administrator', 'auth_type' => 'Authentication method', + 'auth_none' => 'None (dangerous)', 'users_list' => 'List of users', 'language' => 'Language', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index adc38acbe..cad156d47 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -158,13 +158,14 @@ return array ( 'current_user' => 'Utilisateur actuel', 'default_user' => 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)', - 'persona_connection_email' => 'Adresse courriel de connexion (utilise Mozilla Persona)', + 'persona_connection_email' => 'Adresse courriel de connexion (pour Mozilla Persona)', 'allow_anonymous' => 'Autoriser la lecture anonyme pour l’utilisateur par défaut (%s)', 'auth_token' => 'Jeton d’identification', - 'explain_token' => 'Permet d’accéder à la sortie RSS sans besoin de s’authentifier.
    %s?output=rss&token=%s', + 'explain_token' => 'Permet d’accéder à la sortie RSS de l’utilisateur par défaut sans besoin de s’authentifier.
    %s?output=rss&token=%s', 'login_configuration' => 'Identification', 'is_admin' => 'est administrateur', 'auth_type' => 'Méthode d’authentification', + 'auth_none' => 'Aucune (dangereux)', 'users_list' => 'Liste des utilisateurs', 'language' => 'Langue', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index 9a6b16d58..8730baf0e 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -2,14 +2,14 @@
      - conf) || is_logged ()) { ?> + loginOk) { ?>
    • - conf)) { ?> +
    • @@ -69,7 +69,7 @@
    • - conf) || is_logged ()) { ?> + loginOk) { ?>
    • diff --git a/app/layout/header.phtml b/app/layout/header.phtml index aeb417a6e..0f2c524c4 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -1,9 +1,9 @@ -conf)) { ?> + @@ -19,9 +19,7 @@
    diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index cb6579a6b..223f81e8d 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -3,16 +3,15 @@
    -
    +
    - $_SERVER['REMOTE_USER'] =
    @@ -22,21 +21,25 @@ conf->mail_login; ?>
    - + placeholder="alice@example.net" />
    +
    + +
    - + - +
    +
    @@ -46,17 +49,7 @@ -
    -
    - -
    - -
    - + $_SERVER['REMOTE_USER'] = ``
    @@ -67,6 +60,8 @@ + + Mozilla Persona
    @@ -95,4 +90,66 @@ + +
    + + +
    + +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    + + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    + + conf->mail_login; ?> +
    + +
    +
    + +
    +
    + + +
    +
    + +
    + +
    diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 1513af6d0..873908ce6 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -28,7 +28,7 @@ class Minz_Configuration { /** * définition des variables de configuration - * $sel_application une chaîne de caractères aléatoires (obligatoire) + * $salt une chaîne de caractères aléatoires (obligatoire) * $environment gère le niveau d'affichage pour log et erreurs * $use_url_rewriting indique si on utilise l'url_rewriting * $base_url le chemin de base pour accéder à l'application @@ -42,7 +42,7 @@ class Minz_Configuration { * - password mot de passe de l'utilisateur * - base le nom de la base de données */ - private static $sel_application = ''; + private static $salt = ''; private static $environment = Minz_Configuration::PRODUCTION; private static $base_url = ''; private static $use_url_rewriting = false; @@ -55,17 +55,19 @@ class Minz_Configuration { private static $auth_type = 'none'; private static $db = array ( - 'host' => false, - 'user' => false, - 'password' => false, - 'base' => false + 'type' => 'mysql', + 'host' => '', + 'user' => '', + 'password' => '', + 'base' => '', + 'prefix' => '', ); /* * Getteurs */ public static function salt () { - return self::$sel_application; + return self::$salt; } public static function environment () { return self::$environment; @@ -145,7 +147,7 @@ class Minz_Configuration { 'general' => array( 'environment' => self::$environment, 'use_url_rewriting' => self::$use_url_rewriting, - 'sel_application' => self::$sel_application, + 'salt' => self::$salt, 'base_url' => self::$base_url, 'title' => self::$title, 'default_user' => self::$default_user, @@ -189,14 +191,18 @@ class Minz_Configuration { } $general = $ini_array['general']; - // sel_application est obligatoire - if (!isset ($general['sel_application'])) { - throw new Minz_BadConfigurationException ( - 'sel_application', - Minz_Exception::ERROR - ); + // salt est obligatoire + if (!isset ($general['salt'])) { + if (isset($general['sel_application'])) { //v0.6 + $general['salt'] = $general['sel_application']; + } else { + throw new Minz_BadConfigurationException ( + 'salt', + Minz_Exception::ERROR + ); + } } - self::$sel_application = $general['sel_application']; + self::$salt = $general['salt']; if (isset ($general['environment'])) { switch ($general['environment']) { @@ -256,18 +262,15 @@ class Minz_Configuration { } // Base de données - $db = false; if (isset ($ini_array['db'])) { $db = $ini_array['db']; - } - if ($db) { - if (!isset ($db['host'])) { + if (empty($db['host'])) { throw new Minz_BadConfigurationException ( 'host', Minz_Exception::ERROR ); } - if (!isset ($db['user'])) { + if (empty($db['user'])) { throw new Minz_BadConfigurationException ( 'user', Minz_Exception::ERROR @@ -279,19 +282,23 @@ class Minz_Configuration { Minz_Exception::ERROR ); } - if (!isset ($db['base'])) { + if (empty($db['base'])) { throw new Minz_BadConfigurationException ( 'base', Minz_Exception::ERROR ); } - self::$db['type'] = isset ($db['type']) ? $db['type'] : 'mysql'; + if (!empty($db['type'])) { + self::$db['type'] = $db['type']; + } self::$db['host'] = $db['host']; self::$db['user'] = $db['user']; self::$db['password'] = $db['password']; self::$db['base'] = $db['base']; - self::$db['prefix'] = isset ($db['prefix']) ? $db['prefix'] : ''; + if (isset($db['prefix'])) { + self::$db['prefix'] = $db['prefix']; + } } } diff --git a/lib/Minz/FileNotExistException.php b/lib/Minz/FileNotExistException.php index df2b8ff6c..f8dfbdf66 100644 --- a/lib/Minz/FileNotExistException.php +++ b/lib/Minz/FileNotExistException.php @@ -1,7 +1,7 @@ */ @@ -23,7 +23,7 @@ class Minz_ModelPdo { protected $bd; protected $prefix; - + /** * Créé la connexion à la base de données à l'aide des variables * HOST, BASE, USER et PASS définies dans le fichier de configuration @@ -80,11 +80,15 @@ class Minz_ModelPdo { $this->bd->rollBack(); } - public function size() { + public function size($all = false) { $db = Minz_Configuration::dataBase (); $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = ?'; - $stm = $this->bd->prepare ($sql); $values = array ($db['base']); + if (!$all) { + $sql .= ' AND table_name LIKE ?'; + $values[] = $this->prefix . '%'; + } + $stm = $this->bd->prepare ($sql); $stm->execute ($values); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); return $res[0]; diff --git a/p/i/install.php b/p/i/install.php index 672f64b94..e953cf699 100644 --- a/p/i/install.php +++ b/p/i/install.php @@ -12,6 +12,8 @@ if (isset ($_GET['step'])) { define ('STEP', 1); } +define('SQL_CREATE_DB', 'CREATE DATABASE %1$s DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); + include(APP_PATH . '/sql.php'); // @@ -151,7 +153,7 @@ function saveStep2 () { return false; } - $_SESSION['sel_application'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); + $_SESSION['salt'] = sha1(uniqid(mt_rand(), true).implode('', stat(__FILE__))); $_SESSION['title'] = substr(trim($_POST['title']), 0, 25); $_SESSION['old_entries'] = $_POST['old_entries']; if ((!ctype_digit($_SESSION['old_entries'])) || ($_SESSION['old_entries'] < 1)) { @@ -162,7 +164,7 @@ function saveStep2 () { $token = ''; if ($_SESSION['mail_login']) { - $token = sha1($_SESSION['sel_application'] . $_SESSION['mail_login']); + $token = sha1($_SESSION['salt'] . $_SESSION['mail_login']); } $config_array = array ( @@ -173,7 +175,7 @@ function saveStep2 () { ); $configPath = DATA_PATH . '/' . $_SESSION['default_user'] . '_user.php'; - @unlink(configPath); //To avoid access-rights problems + @unlink($configPath); //To avoid access-rights problems file_put_contents($configPath, " array( 'environment' => empty($_SESSION['environment']) ? 'production' : $_SESSION['environment'], 'use_url_rewriting' => false, - 'sel_application' => $_SESSION['sel_application'], + 'salt' => $_SESSION['salt'], 'base_url' => '', 'title' => $_SESSION['title'], 'default_user' => $_SESSION['default_user'], @@ -424,7 +426,7 @@ function checkStep0 () { if ($ini_array) { $ini_general = isset($ini_array['general']) ? $ini_array['general'] : null; if ($ini_general) { - $keys = array('environment', 'sel_application', 'title', 'default_user'); + $keys = array('environment', 'salt', 'title', 'default_user'); foreach ($keys as $key) { if ((empty($_SESSION[$key])) && isset($ini_general[$key])) { $_SESSION[$key] = $ini_general[$key]; @@ -496,7 +498,7 @@ function checkStep1 () { } function checkStep2 () { - $conf = !empty($_SESSION['sel_application']) && + $conf = !empty($_SESSION['salt']) && !empty($_SESSION['title']) && !empty($_SESSION['old_entries']) && isset($_SESSION['mail_login']) && @@ -537,7 +539,7 @@ function checkStep3 () { } function checkBD () { - $error = false; + $ok = false; try { $str = ''; @@ -575,35 +577,18 @@ function checkBD () { $res = $c->query($sql); //Backup tables } - $sql = sprintf (SQL_CAT, $_SESSION['bd_prefix_user']); - $res = $c->query ($sql); - - if (!$res) { - $error = true; - } - - $sql = sprintf (SQL_FEED, $_SESSION['bd_prefix_user']); - $res = $c->query ($sql); - - if (!$res) { - $error = true; - } - - $sql = sprintf (SQL_ENTRY, $_SESSION['bd_prefix_user']); - $res = $c->query ($sql); - - if (!$res) { - $error = true; - } + $sql = sprintf(SQL_CREATE_TABLES, $_SESSION['bd_prefix_user']); + $stm = $c->prepare($sql, array(PDO::ATTR_EMULATE_PREPARES => true)); + $ok = $stm->execute(); } catch (PDOException $e) { $error = true; } - if ($error && file_exists (DATA_PATH . '/config.php')) { - unlink (DATA_PATH . '/config.php'); + if (!$ok) { + @unlink(DATA_PATH . '/config.php'); } - return !$error; + return $ok; } /*** AFFICHAGE ***/ @@ -729,9 +714,6 @@ function printStep2 () {
    -
    -- cgit v1.2.3 From 5c9a32329ad68dc5ae8bc8a3566a0d603b80a934 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 31 Dec 2013 14:52:01 +0100 Subject: Multi-utilisateur fonctionnel avec Mozilla Persona MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il faut ré-enregistrer l'adresse courriel une fois dans l'interface de FreshRSS pour créer le fichier nécessaire. + Comparaison sans tenir compte de la casse pour les noms d'utilisateur. Contribue à https://github.com/marienfressinaud/FreshRSS/issues/126 ll faudra tester la sécurité --- app/Controllers/indexController.php | 32 +++++++++++++++++++++++++++++--- app/Controllers/usersController.php | 37 ++++++++++++++++++++++++++----------- app/FreshRSS.php | 18 ++++++++++++++---- data/cache/index.html | 13 +++++++++++++ data/favicons/index.html | 13 +++++++++++++ data/log/index.html | 13 +++++++++++++ data/persona/.gitignore | 1 + data/persona/index.html | 13 +++++++++++++ p/i/install.php | 6 ++++++ 9 files changed, 128 insertions(+), 18 deletions(-) create mode 100644 data/cache/index.html create mode 100644 data/favicons/index.html create mode 100644 data/log/index.html create mode 100644 data/persona/.gitignore create mode 100644 data/persona/index.html (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 7309169a6..5b51b3e28 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -249,14 +249,40 @@ class FreshRSS_index_Controller extends Minz_ActionController { curl_close ($ch); $res = json_decode ($result, true); - if ($res['status'] === 'okay' && $res['email'] === $this->view->conf->mail_login) { - Minz_Session::_param ('mail', $res['email']); + + $loginOk = false; + $reason = ''; + if ($res['status'] === 'okay') { + $email = filter_var($res['email'], FILTER_VALIDATE_EMAIL); + if ($email != '') { + $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; + if (($currentUser = @file_get_contents($personaFile)) !== false) { + $currentUser = trim($currentUser); + if (ctype_alnum($currentUser)) { + try { + $this->conf = new FreshRSS_Configuration($currentUser); + $loginOk = strcasecmp($email, $this->conf->mail_login) === 0; + } catch (Minz_Exception $e) { + $reason = 'Invalid configuration for user [' . $currentUser . ']! ' . $e->getMessage(); //Permission denied or conf file does not exist + } + } else { + $reason = 'Invalid username format [' . $currentUser . ']!'; + } + } + } else { + $reason = 'Invalid email format [' . $res['email'] . ']!'; + } + } + if ($loginOk) { + Minz_Session::_param('currentUser', $currentUser); + Minz_Session::_param ('mail', $email); $this->view->loginOk = true; invalidateHttpCache(); } else { $res = array (); $res['status'] = 'failure'; - $res['reason'] = Minz_Translate::t ('invalid_login'); + $res['reason'] = $reason == '' ? Minz_Translate::t ('invalid_login') : $reason; + Minz_Log::record ('Persona: ' . $res['reason'], Minz_Log::WARNING); } header('Content-Type: application/json; charset=UTF-8'); diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 7d9568083..5b3ffe81a 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -17,7 +17,14 @@ class FreshRSS_users_Controller extends Minz_ActionController { $this->view->conf->_mail_login($mail); $ok &= $this->view->conf->save(); - Minz_Session::_param('mail', $this->view->conf->mail_login); + $email = $this->view->conf->mail_login; + Minz_Session::_param('mail', $email); + + if ($email != '') { + $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; + @unlink($personaFile); + $ok &= (file_put_contents($personaFile, Minz_Session::param('currentUser', '_')) !== false); + } //TODO: use $ok $notif = array( @@ -38,8 +45,6 @@ class FreshRSS_users_Controller extends Minz_ActionController { $this->view->conf->_token($token); $ok &= $this->view->conf->save(); - Minz_Session::_param('mail', $this->view->conf->mail_login); - $anon = Minz_Request::param('anon_access', false); $anon = ((bool)$anon) && ($anon !== 'no'); $auth_type = Minz_Request::param('auth_type', 'none'); @@ -69,18 +74,27 @@ class FreshRSS_users_Controller extends Minz_ActionController { } $new_user_name = Minz_Request::param('new_user_name'); - $ok = ctype_alnum($new_user_name); - - $new_user_email = filter_var($_POST['new_user_email'], FILTER_VALIDATE_EMAIL); - if (empty($new_user_email)) { - $new_user_email = ''; - $ok &= Minz_Configuration::authType() !== 'persona'; - } + $ok = ($new_user_name != '') && ctype_alnum($new_user_name); if ($ok) { + $ok &= (strcasecmp($new_user_name, Minz_Configuration::defaultUser()) !== 0); //It is forbidden to alter the default user + + $ok &= !in_array(strtoupper($new_user_name), array_map('strtoupper', listUsers())); //Not an existing user, case-insensitive + $configPath = DATA_PATH . '/' . $new_user_name . '_user.php'; $ok &= !file_exists($configPath); } + if ($ok) { + $new_user_email = filter_var($_POST['new_user_email'], FILTER_VALIDATE_EMAIL); + if (empty($new_user_email)) { + $new_user_email = ''; + $ok &= Minz_Configuration::authType() !== 'persona'; + } else { + $personaFile = DATA_PATH . '/persona/' . $new_user_email . '.txt'; + @unlink($personaFile); + $ok &= (file_put_contents($personaFile, $new_user_name) !== false); + } + } if ($ok) { $config_array = array( 'language' => $new_user_language, @@ -110,7 +124,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $ok = ctype_alnum($username); if ($ok) { - $ok &= ($username !== Minz_Configuration::defaultUser()); //It is forbidden to delete the default user + $ok &= (strcasecmp($username, Minz_Configuration::defaultUser()) !== 0); //It is forbidden to delete the default user } if ($ok) { $configPath = DATA_PATH . '/' . $username . '_user.php'; @@ -120,6 +134,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->deleteUser($username); $ok &= unlink($configPath); + //TODO: delete Persona file } $notif = array( 'type' => $ok ? 'good' : 'bad', diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 0e166cc3b..0af0c01da 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -18,8 +18,18 @@ class FreshRSS extends Minz_FrontController { $loginOk = $currentUser != ''; break; case 'persona': - $currentUser = Minz_Configuration::defaultUser(); //TODO: Make Persona compatible with multi-user - $loginOk = Minz_Session::param('mail') != ''; + $loginOk = false; + $email = filter_var(Minz_Session::param('mail'), FILTER_VALIDATE_EMAIL); + if ($email != '') { //TODO: Remove redundancy with indexController + $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; + if (($currentUser = @file_get_contents($personaFile)) !== false) { + $currentUser = trim($currentUser); + $loginOk = true; + } + } + if (!$loginOk) { + $currentUser = Minz_Configuration::defaultUser(); + } break; case 'none': $currentUser = Minz_Configuration::defaultUser(); @@ -51,10 +61,10 @@ class FreshRSS extends Minz_FrontController { if ($loginOk) { switch (Minz_Configuration::authType()) { case 'http_auth': - $loginOk = $currentUser === httpAuthUser(); + $loginOk = strcasecmp($currentUser, httpAuthUser()) === 0; break; case 'persona': - $loginOk = Minz_Session::param('mail') === $this->conf->mail_login; + $loginOk = strcasecmp(Minz_Session::param('mail'), $this->conf->mail_login) === 0; break; case 'none': $loginOk = true; diff --git a/data/cache/index.html b/data/cache/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/data/cache/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

    Redirection

    + + diff --git a/data/favicons/index.html b/data/favicons/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/data/favicons/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

    Redirection

    + + diff --git a/data/log/index.html b/data/log/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/data/log/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

    Redirection

    + + diff --git a/data/persona/.gitignore b/data/persona/.gitignore new file mode 100644 index 000000000..314f02b1b --- /dev/null +++ b/data/persona/.gitignore @@ -0,0 +1 @@ +*.txt \ No newline at end of file diff --git a/data/persona/index.html b/data/persona/index.html new file mode 100644 index 000000000..85faaa37e --- /dev/null +++ b/data/persona/index.html @@ -0,0 +1,13 @@ + + + + + +Redirection + + + + +

    Redirection

    + + diff --git a/p/i/install.php b/p/i/install.php index e953cf699..0cd952fef 100644 --- a/p/i/install.php +++ b/p/i/install.php @@ -178,6 +178,12 @@ function saveStep2 () { @unlink($configPath); //To avoid access-rights problems file_put_contents($configPath, " Date: Tue, 31 Dec 2013 15:21:39 +0100 Subject: Ajouts de quelques invalidateHttpCache --- app/Controllers/configureController.php | 7 +++++++ app/Controllers/usersController.php | 5 +++++ 2 files changed, 12 insertions(+) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 17abf6b89..0e4801a2d 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -50,6 +50,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $catDAO->addCategory ($values); } } + invalidateHttpCache(); $notif = array ( 'type' => 'good', @@ -124,6 +125,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'content' => Minz_Translate::t ('error_occurred_update') ); } + invalidateHttpCache(); Minz_Session::_param ('notification', $notif); Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => array ('id' => $id)), true); @@ -168,6 +170,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Session::_param ('language', $this->view->conf->language); Minz_Translate::reset (); + invalidateHttpCache(); $notif = array ( 'type' => 'good', @@ -196,6 +199,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'print' => Minz_Request::param ('print', false), )); $this->view->conf->save(); + invalidateHttpCache(); $notif = array ( 'type' => 'good', @@ -235,6 +239,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->categories = $list; } elseif ($this->view->req == 'import' && Minz_Request::isPost ()) { if ($_FILES['file']['error'] == 0) { + invalidateHttpCache(); // on parse le fichier OPML pour récupérer les catégories et les flux associés try { list ($categories, $feeds) = opml_import ( @@ -295,6 +300,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_shortcuts ($shortcuts_ok); $this->view->conf->save(); + invalidateHttpCache(); $notif = array ( 'type' => 'good', @@ -320,6 +326,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->conf->_old_entries($old); $this->view->conf->_keep_history_default($keepHistoryDefault); $this->view->conf->save(); + invalidateHttpCache(); $notif = array( 'type' => 'good', diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 5b3ffe81a..0ce3b3447 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -25,6 +25,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { @unlink($personaFile); $ok &= (file_put_contents($personaFile, Minz_Session::param('currentUser', '_')) !== false); } + invalidateHttpCache(); //TODO: use $ok $notif = array( @@ -54,6 +55,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { Minz_Configuration::_authType($auth_type); $ok &= Minz_Configuration::writeFile(); } + invalidateHttpCache(); $notif = array( 'type' => $ok ? 'good' : 'bad', @@ -106,6 +108,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->createUser($new_user_name); } + invalidateHttpCache(); $notif = array( 'type' => $ok ? 'good' : 'bad', @@ -136,6 +139,8 @@ class FreshRSS_users_Controller extends Minz_ActionController { $ok &= unlink($configPath); //TODO: delete Persona file } + invalidateHttpCache(); + $notif = array( 'type' => $ok ? 'good' : 'bad', 'content' => Minz_Translate::t($ok ? 'user_deleted' : 'error_occurred', $username) -- cgit v1.2.3 From 50c41d9bb2b1766feac984685bb2a954ab4799f3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 1 Jan 2014 13:48:32 +0100 Subject: Détails multi-utilisateur MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/marienfressinaud/FreshRSS/issues/126 --- app/Controllers/usersController.php | 47 ++++++++++++++----------------------- app/views/configure/archiving.phtml | 4 ++-- app/views/configure/feed.phtml | 4 ++-- app/views/configure/users.phtml | 9 ++++--- p/themes/default/global.css | 2 +- p/themes/flat-design/global.css | 2 +- 6 files changed, 28 insertions(+), 40 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 0ce3b3447..482e35c3e 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -10,9 +10,10 @@ class FreshRSS_users_Controller extends Minz_ActionController { } } - public function idAction() { + public function authAction() { if (Minz_Request::isPost()) { $ok = true; + $mail = Minz_Request::param('mail_login', false); $this->view->conf->_mail_login($mail); $ok &= $this->view->conf->save(); @@ -25,36 +26,24 @@ class FreshRSS_users_Controller extends Minz_ActionController { @unlink($personaFile); $ok &= (file_put_contents($personaFile, Minz_Session::param('currentUser', '_')) !== false); } - invalidateHttpCache(); - //TODO: use $ok - $notif = array( - 'type' => 'good', - 'content' => Minz_Translate::t('configuration_updated') - ); - Minz_Session::_param('notification', $notif); - - Minz_Request::forward(array('c' => 'configure', 'a' => 'users'), true); - } - } - - public function authAction() { - if (Minz_Request::isPost() && Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { - $ok = true; - $current_token = $this->view->conf->token; - $token = Minz_Request::param('token', $current_token); - $this->view->conf->_token($token); - $ok &= $this->view->conf->save(); - - $anon = Minz_Request::param('anon_access', false); - $anon = ((bool)$anon) && ($anon !== 'no'); - $auth_type = Minz_Request::param('auth_type', 'none'); - if ($anon != Minz_Configuration::allowAnonymous() || - $auth_type != Minz_Configuration::authType()) { - Minz_Configuration::_allowAnonymous($anon); - Minz_Configuration::_authType($auth_type); - $ok &= Minz_Configuration::writeFile(); + if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + $current_token = $this->view->conf->token; + $token = Minz_Request::param('token', $current_token); + $this->view->conf->_token($token); + $ok &= $this->view->conf->save(); + + $anon = Minz_Request::param('anon_access', false); + $anon = ((bool)$anon) && ($anon !== 'no'); + $auth_type = Minz_Request::param('auth_type', 'none'); + if ($anon != Minz_Configuration::allowAnonymous() || + $auth_type != Minz_Configuration::authType()) { + Minz_Configuration::_allowAnonymous($anon); + Minz_Configuration::_authType($auth_type); + $ok &= Minz_Configuration::writeFile(); + } } + invalidateHttpCache(); $notif = array( diff --git a/app/views/configure/archiving.phtml b/app/views/configure/archiving.phtml index cbe3b086c..6e26ca81e 100644 --- a/app/views/configure/archiving.phtml +++ b/app/views/configure/archiving.phtml @@ -17,8 +17,8 @@
    - '', 0 => '0', 10 => '10', 50 => '50', 100 => '100', 500 => '500', 1000 => '1 000', 5000 => '5 000', 10000 => '10 000', -1 => '∞') as $v => $t) { echo ''; } ?> () diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index e738ab64f..a0fe39f8a 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -87,8 +87,8 @@
    - '', -2 => Minz_Translate::t('by_default'), 0 => '0', 10 => '10', 50 => '50', 100 => '100', 500 => '500', 1000 => '1 000', 5000 => '5 000', 10000 => '10 000', -1 => '∞') as $v => $t) { echo ''; } ?> diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 223f81e8d..d40a3ad5b 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -3,7 +3,7 @@
    - +
    @@ -34,20 +34,19 @@
    - -
    - + - + $_SERVER['REMOTE_USER'] = ``
    diff --git a/p/themes/default/global.css b/p/themes/default/global.css index 913da4b27..2cc195f90 100644 --- a/p/themes/default/global.css +++ b/p/themes/default/global.css @@ -112,7 +112,7 @@ input, select, textarea { border-color: #33BBFF; box-shadow: 0 2px 2px #DDDDFF inset; } - input:invalid { + input:invalid, select:invalid { border-color: red; box-shadow: 0 0 2px 1px red; } diff --git a/p/themes/flat-design/global.css b/p/themes/flat-design/global.css index cb9495865..c24a9f481 100644 --- a/p/themes/flat-design/global.css +++ b/p/themes/flat-design/global.css @@ -113,7 +113,7 @@ input, select, textarea { color: #333; border-color: #2980b9; } - input:invalid { + input:invalid, select:invalid { border-color: red; box-shadow: 0 0 2px 1px red; } -- cgit v1.2.3 From 3b23ca881c63fe6e1cc8ef98ae5a5af645d4870a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 2 Jan 2014 14:38:06 +0100 Subject: Mutex pour actualize_script Nouveau fichier temporaire ./data/actualize.lock.txt --- app/Controllers/indexController.php | 2 +- app/FreshRSS.php | 2 +- app/actualize_script.php | 20 ++++++++++++++++++-- data/.gitignore | 1 + 4 files changed, 21 insertions(+), 4 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 5b51b3e28..690ca57be 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -133,7 +133,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles - if ($state === 'not_read' && empty($entries)) { //TODO: Remove in v0.8 + if ($state === 'not_read' && empty($entries)) { Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $this->view->state = 'all'; $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault); diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 4ebb6d631..f9857a4cb 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -6,7 +6,7 @@ class FreshRSS extends Minz_FrontController { } $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); - $this->loadStylesAndScripts(); + $this->loadStylesAndScripts(); //TODO: Do not load that when not needed, e.g. some Ajax requests $this->loadNotifications(); } diff --git a/app/actualize_script.php b/app/actualize_script.php index e0f995afe..6009ad849 100755 --- a/app/actualize_script.php +++ b/app/actualize_script.php @@ -1,7 +1,19 @@ +$lock = DATA_PATH . '/actualize.lock.txt'; +if (file_exists($lock) && ((time() - @filemtime($lock)) > 3600)) { + @unlink($lock); +} +if (($handle = @fopen($lock, 'x')) === false) { + syslog(LOG_INFO, 'FreshRSS actualize already running?'); + return; +} +register_shutdown_function('unlink', $lock); +//Could use http://php.net/function.pcntl-signal.php to catch interruptions +@fclose($handle); +// require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader @@ -10,8 +22,12 @@ ob_implicit_flush(false); ob_start(); echo 'Results: ', "\n"; //Buffered +Minz_Configuration::init(); + $users = listUsers(); -shuffle($users); +shuffle($users); //Process users in random order +array_unshift($users, Minz_Configuration::defaultUser()); //But always start with admin +$users = array_unique($users); foreach ($users as $myUser) { syslog(LOG_INFO, 'FreshRSS actualize ' . $myUser); diff --git a/data/.gitignore b/data/.gitignore index 005982b00..afb16d9aa 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -5,3 +5,4 @@ config.php touch.txt no-cache.txt *.bak.php +*.lock.txt -- cgit v1.2.3 From 4972d507f1d7c2a3c59d4884be891fba2ca8e5ee Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 2 Jan 2014 22:21:49 +0100 Subject: Bug changement catégories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduit récemment --- app/Controllers/configureController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 0e4801a2d..0a403fc2d 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -100,12 +100,14 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $httpAuth = $user . ':' . $pass; } + $cat = intval(Minz_Request::param('category', 0)); + $values = array ( 'name' => Minz_Request::param ('name', ''), 'description' => sanitizeHTML(Minz_Request::param('description', '', true)), 'website' => Minz_Request::param('website', ''), 'url' => Minz_Request::param('url', ''), - 'category' => intval(Minz_Request::param ('category', 0)), + 'category' => $cat, 'pathEntries' => Minz_Request::param ('path_entries', ''), 'priority' => intval(Minz_Request::param ('priority', 0)), 'httpAuth' => $httpAuth, -- cgit v1.2.3 From 1fb25addd09660c08ff74b960b1c226acc319978 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 5 Jan 2014 17:35:38 +0100 Subject: Debug Persona temporaire https://github.com/marienfressinaud/FreshRSS/issues/347 --- app/Controllers/indexController.php | 5 +++++ app/FreshRSS.php | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 690ca57be..9817b7a81 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -237,6 +237,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $assert = Minz_Request::param ('assertion'); $params = 'assertion=' . $assert . '&audience=' . urlencode (Minz_Url::display (null, 'php', true)); + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' login params=' . print_r($params, true) . "\n", FILE_APPEND); //DEBUG $ch = curl_init (); $options = array ( CURLOPT_URL => $url, @@ -252,8 +253,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { $loginOk = false; $reason = ''; + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' login response=' . print_r($res, true) . "\n", FILE_APPEND); //DEBUG if ($res['status'] === 'okay') { $email = filter_var($res['email'], FILTER_VALIDATE_EMAIL); + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' filtered_email=' . $email . "\n", FILE_APPEND); //DEBUG if ($email != '') { $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; if (($currentUser = @file_get_contents($personaFile)) !== false) { @@ -277,12 +280,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser', $currentUser); Minz_Session::_param ('mail', $email); $this->view->loginOk = true; + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Login OK email=' . $email . ', currentUser=' . $currentUser . "\n", FILE_APPEND); //DEBUG invalidateHttpCache(); } else { $res = array (); $res['status'] = 'failure'; $res['reason'] = $reason == '' ? Minz_Translate::t ('invalid_login') : $reason; Minz_Log::record ('Persona: ' . $res['reason'], Minz_Log::WARNING); + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Failure=' . print_r($res, true) . "\n", FILE_APPEND); //DEBUG } header('Content-Type: application/json; charset=UTF-8'); diff --git a/app/FreshRSS.php b/app/FreshRSS.php index f9857a4cb..86d35a6e7 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -11,6 +11,7 @@ class FreshRSS extends Minz_FrontController { } private function accessControl($currentUser) { + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' authType=' . Minz_Configuration::authType() . "\n", FILE_APPEND); //DEBUG if ($currentUser == '') { switch (Minz_Configuration::authType()) { case 'http_auth': @@ -18,12 +19,15 @@ class FreshRSS extends Minz_FrontController { $loginOk = $currentUser != ''; break; case 'persona': + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona access control' . "\n", FILE_APPEND); //DEBUG $loginOk = false; $email = filter_var(Minz_Session::param('mail'), FILTER_VALIDATE_EMAIL); + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona email=' . Minz_Session::param('mail') . ', filtered_email=' . $email . "\n", FILE_APPEND); if ($email != '') { //TODO: Remove redundancy with indexController $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; if (($currentUser = @file_get_contents($personaFile)) !== false) { $currentUser = trim($currentUser); + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona user from file=' . $currentUser . "\n", FILE_APPEND); //DEBUG $loginOk = true; } } @@ -77,6 +81,7 @@ class FreshRSS extends Minz_FrontController { $loginOk = strcasecmp($currentUser, httpAuthUser()) === 0; break; case 'persona': + file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona compare session_email=' . Minz_Session::param('mail') . ' with config_email=' . $this->conf->mail_login . "\n", FILE_APPEND); //DEBUG $loginOk = strcasecmp(Minz_Session::param('mail'), $this->conf->mail_login) === 0; break; case 'none': -- cgit v1.2.3 From ae386823abf8ec095f7828e29f5b2a8981353fba Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 7 Jan 2014 18:00:03 +0100 Subject: Revert "Debug Persona temporaire" This reverts commit 1fb25addd09660c08ff74b960b1c226acc319978. --- app/Controllers/indexController.php | 5 ----- app/FreshRSS.php | 5 ----- 2 files changed, 10 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 9817b7a81..690ca57be 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -237,7 +237,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { $assert = Minz_Request::param ('assertion'); $params = 'assertion=' . $assert . '&audience=' . urlencode (Minz_Url::display (null, 'php', true)); - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' login params=' . print_r($params, true) . "\n", FILE_APPEND); //DEBUG $ch = curl_init (); $options = array ( CURLOPT_URL => $url, @@ -253,10 +252,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { $loginOk = false; $reason = ''; - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' login response=' . print_r($res, true) . "\n", FILE_APPEND); //DEBUG if ($res['status'] === 'okay') { $email = filter_var($res['email'], FILTER_VALIDATE_EMAIL); - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' filtered_email=' . $email . "\n", FILE_APPEND); //DEBUG if ($email != '') { $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; if (($currentUser = @file_get_contents($personaFile)) !== false) { @@ -280,14 +277,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_Session::_param('currentUser', $currentUser); Minz_Session::_param ('mail', $email); $this->view->loginOk = true; - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Login OK email=' . $email . ', currentUser=' . $currentUser . "\n", FILE_APPEND); //DEBUG invalidateHttpCache(); } else { $res = array (); $res['status'] = 'failure'; $res['reason'] = $reason == '' ? Minz_Translate::t ('invalid_login') : $reason; Minz_Log::record ('Persona: ' . $res['reason'], Minz_Log::WARNING); - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Failure=' . print_r($res, true) . "\n", FILE_APPEND); //DEBUG } header('Content-Type: application/json; charset=UTF-8'); diff --git a/app/FreshRSS.php b/app/FreshRSS.php index 86d35a6e7..f9857a4cb 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -11,7 +11,6 @@ class FreshRSS extends Minz_FrontController { } private function accessControl($currentUser) { - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' authType=' . Minz_Configuration::authType() . "\n", FILE_APPEND); //DEBUG if ($currentUser == '') { switch (Minz_Configuration::authType()) { case 'http_auth': @@ -19,15 +18,12 @@ class FreshRSS extends Minz_FrontController { $loginOk = $currentUser != ''; break; case 'persona': - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona access control' . "\n", FILE_APPEND); //DEBUG $loginOk = false; $email = filter_var(Minz_Session::param('mail'), FILTER_VALIDATE_EMAIL); - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona email=' . Minz_Session::param('mail') . ', filtered_email=' . $email . "\n", FILE_APPEND); if ($email != '') { //TODO: Remove redundancy with indexController $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; if (($currentUser = @file_get_contents($personaFile)) !== false) { $currentUser = trim($currentUser); - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona user from file=' . $currentUser . "\n", FILE_APPEND); //DEBUG $loginOk = true; } } @@ -81,7 +77,6 @@ class FreshRSS extends Minz_FrontController { $loginOk = strcasecmp($currentUser, httpAuthUser()) === 0; break; case 'persona': - file_put_contents(DATA_PATH . '/log/persona.log', date('c') . ' Persona compare session_email=' . Minz_Session::param('mail') . ' with config_email=' . $this->conf->mail_login . "\n", FILE_APPEND); //DEBUG $loginOk = strcasecmp(Minz_Session::param('mail'), $this->conf->mail_login) === 0; break; case 'none': -- cgit v1.2.3 From b381d2a592ebe4e3446e342fdcf961d76f33e89a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 7 Jan 2014 20:38:45 +0100 Subject: Un peu de typographie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remplace les tirets de soustraction par points médians ou des tirets cadratins (si c'est trop long, nous pourrions mettre des demi-cadratins). * Met les abréviations des jours anglais en exposant, comme `3rd` --- app/Controllers/configureController.php | 18 +++++++++--------- app/Controllers/errorController.php | 2 +- app/Controllers/indexController.php | 6 +++--- app/i18n/en.php | 4 ++-- app/i18n/fr.php | 4 ++-- app/i18n/install.en.php | 4 ++-- app/i18n/install.fr.php | 4 ++-- app/views/configure/display.phtml | 8 ++++---- app/views/helpers/view/normal_view.phtml | 6 +++--- app/views/helpers/view/reader_view.phtml | 2 +- app/views/index/about.phtml | 2 +- p/scripts/main.js | 2 +- 12 files changed, 31 insertions(+), 31 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 0a403fc2d..5b5770cbe 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -66,7 +66,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->feeds = $feedDAO->listFeeds (); $this->view->flux = false; - Minz_View::prependTitle (Minz_Translate::t ('categories_management') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('categories_management') . ' · '); } public function feedAction () { @@ -133,10 +133,10 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Request::forward (array ('c' => 'configure', 'a' => 'feed', 'params' => array ('id' => $id)), true); } - Minz_View::prependTitle (Minz_Translate::t ('rss_feed_management') . ' - ' . $this->view->flux->name () . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('rss_feed_management') . ' — ' . $this->view->flux->name () . ' · '); } } else { - Minz_View::prependTitle (Minz_Translate::t ('rss_feed_management') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('rss_feed_management') . ' · '); } } @@ -185,7 +185,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $this->view->themes = FreshRSS_Themes::get(); - Minz_View::prependTitle (Minz_Translate::t ('reading_configuration') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('reading_configuration') . ' · '); } public function sharingAction () { @@ -212,7 +212,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Request::forward (array ('c' => 'configure', 'a' => 'sharing'), true); } - Minz_View::prependTitle (Minz_Translate::t ('sharing_management') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('sharing') . ' · '); } public function importExportAction () { @@ -277,7 +277,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { // au niveau de la vue, permet de ne pas voir un flux sélectionné dans la liste $this->view->flux = false; - Minz_View::prependTitle (Minz_Translate::t ('import_export_opml') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('import_export_opml') . ' · '); } public function shortcutAction () { @@ -313,11 +313,11 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Request::forward (array ('c' => 'configure', 'a' => 'shortcut'), true); } - Minz_View::prependTitle (Minz_Translate::t ('shortcuts_management') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('shortcuts') . ' · '); } public function usersAction() { - Minz_View::prependTitle(Minz_Translate::t ('users') . ' - '); + Minz_View::prependTitle(Minz_Translate::t ('users') . ' · '); } public function archivingAction () { @@ -339,7 +339,7 @@ class FreshRSS_configure_Controller extends Minz_ActionController { Minz_Request::forward(array('c' => 'configure', 'a' => 'archiving'), true); } - Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' - '); + Minz_View::prependTitle(Minz_Translate::t('archiving_configuration') . ' · '); $entryDAO = new FreshRSS_EntryDAO(); $this->view->nb_total = $entryDAO->count(); diff --git a/app/Controllers/errorController.php b/app/Controllers/errorController.php index d1c2f8fec..dc9a2ee25 100644 --- a/app/Controllers/errorController.php +++ b/app/Controllers/errorController.php @@ -21,6 +21,6 @@ class FreshRSS_error_Controller extends Minz_ActionController { $this->view->logs = Minz_Request::param ('logs'); - Minz_View::prependTitle ($this->view->code . ' - '); + Minz_View::prependTitle ($this->view->code . ' · '); } } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 690ca57be..81dfefabb 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -83,7 +83,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_View::prependTitle ( $this->view->currentName . ($this->nb_not_read_cat > 0 ? ' (' . $this->nb_not_read_cat . ')' : '') . - ' - ' + ' · ' ); // On récupère les différents éléments de filtrage @@ -204,7 +204,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function aboutAction () { - Minz_View::prependTitle (Minz_Translate::t ('about') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('about') . ' · '); } public function logsAction () { @@ -215,7 +215,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { ); } - Minz_View::prependTitle (Minz_Translate::t ('logs') . ' - '); + Minz_View::prependTitle (Minz_Translate::t ('logs') . ' · '); if (Minz_Request::isPost ()) { FreshRSS_LogDAO::truncate(); diff --git a/app/i18n/en.php b/app/i18n/en.php index 06bbf48e6..4588a054a 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -287,6 +287,6 @@ return array ( 'Nov' => '\N\o\v\e\m\b\e\r', 'Dec' => '\D\e\c\e\m\b\e\r', // format for date() function, %s allows to indicate month in letter - 'format_date' => '%s dS Y', - 'format_date_hour' => '%s dS Y \a\t H\.i', + 'format_date' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y', + 'format_date_hour' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y \a\t H\.i', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index cb689610b..20085e6b4 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -287,6 +287,6 @@ return array ( 'Nov' => '\n\o\v\e\m\b\r\e', 'Dec' => '\d\é\c\e\m\b\r\e', // format pour la fonction date(), %s permet d'indiquer le mois en toutes lettres - 'format_date' => 'd %s Y', - 'format_date_hour' => '\l\e d %s Y \à H\:i', + 'format_date' => 'j %s Y', + 'format_date_hour' => '\l\e j %s Y \à H\:i', ); diff --git a/app/i18n/install.en.php b/app/i18n/install.en.php index 8145abffa..24335c6c9 100644 --- a/app/i18n/install.en.php +++ b/app/i18n/install.en.php @@ -1,8 +1,8 @@ 'Installation - FreshRSS', + 'freshrss_installation' => 'Installation · FreshRSS', 'freshrss' => 'FreshRSS', - 'installation_step' => 'Installation - step %d', + 'installation_step' => 'Installation — step %d · FreshRSS', 'steps' => 'Steps', 'checks' => 'Checks', 'general_configuration' => 'General configuration', diff --git a/app/i18n/install.fr.php b/app/i18n/install.fr.php index 3211ddf19..554d17587 100644 --- a/app/i18n/install.fr.php +++ b/app/i18n/install.fr.php @@ -1,8 +1,8 @@ 'Installation - FreshRSS', + 'freshrss_installation' => 'Installation · FreshRSS', 'freshrss' => 'FreshRSS', - 'installation_step' => 'Installation - étape %d', + 'installation_step' => 'Installation — étape %d · FreshRSS', 'steps' => 'Étapes', 'checks' => 'Vérifications', 'general_configuration' => 'Configuration générale', diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 11a987610..3cc5442fb 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -24,7 +24,7 @@ @@ -81,7 +81,7 @@
    @@ -91,7 +91,7 @@
    @@ -101,7 +101,7 @@
    diff --git a/app/views/helpers/view/normal_view.phtml b/app/views/helpers/view/normal_view.phtml index 5e46f3c8e..7e4457ecc 100644 --- a/app/views/helpers/view/normal_view.phtml +++ b/app/views/helpers/view/normal_view.phtml @@ -43,14 +43,14 @@ if (!empty($this->entries)) { isDay (FreshRSS_Days::TODAY, $this->today)) { ?>
    - - + currentName; ?>
    isDay (FreshRSS_Days::YESTERDAY, $this->today)) { ?>
    - - + currentName; ?>
    @@ -118,7 +118,7 @@ if (!empty($this->entries)) { link ()); - $title = urlencode ($item->title () . ' - ' . $feed->name ()); + $title = urlencode ($item->title () . ' · ' . $feed->name ()); ?>
    +
    + +
    + + +
    +
    +
    conf->mail_login; ?> -- cgit v1.2.3 From d58886a937cbe425163526fc2ba3d2a118602035 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 12 Jan 2014 03:10:31 +0100 Subject: Implémentation de l'indentification par mot de passe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémentation de https://github.com/marienfressinaud/FreshRSS/issues/104 --- CHANGELOG | 3 ++ app/Controllers/indexController.php | 52 +++++++++++++++++++++++++++-- app/Controllers/javascriptController.php | 12 +++---- app/Controllers/usersController.php | 26 ++++++++++++--- app/FreshRSS.php | 27 ++++++++++++--- app/i18n/en.php | 7 ++-- app/i18n/fr.php | 7 ++-- app/layout/header.phtml | 51 +++++++++++++++++++--------- app/views/configure/users.phtml | 16 ++++++--- app/views/helpers/javascript_vars.phtml | 4 +-- app/views/helpers/view/login.phtml | 43 ++++++++++++++++++++++++ app/views/index/index.phtml | 14 ++------ lib/Minz/Configuration.php | 3 +- lib/Minz/FrontController.php | 2 +- p/scripts/main.js | 57 ++++++++++++++++++++++++++++++-- 15 files changed, 265 insertions(+), 59 deletions(-) create mode 100644 app/views/helpers/view/login.phtml (limited to 'app/Controllers') diff --git a/CHANGELOG b/CHANGELOG index fe856fe4a..5c9b56465 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,8 @@ * Nouveau mode multi-utilisateur * L’utilisateur par défaut (administrateur) peut créer et supprimer d’autres utilisateurs * Nécessite un contrôle d’accès, soit : + * par le nouveau mode de connexion par formulaire (nom d’utilisateur + mot de passe) + * relativement sûr même sans HTTPS (le mot de passe n’est pas transmis en clair) * par HTTP (par exemple sous Apache en créant un fichier ./p/i/.htaccess et .htpasswd) * le nom d’utilisateur HTTP doit correspondre au nom d’utilisateur FreshRSS * par Mozilla Persona, en renseignant l’adresse courriel des utilisateurs @@ -68,6 +70,7 @@ * Réorganisation des fichiers et répertoires, en particulier : * Tous les fichiers utilisateur sont dans “./data/” (y compris “cache”, “favicons”, et “log”) * Déplacement de “./app/configuration/application.ini” vers “./data/config.php” + * Meilleure sécurité et compatibilité * Déplacement de “./public/data/Configuration.array.php” vers “./data/*_user.php” * Déplacement de “./public/” vers “./p/” * Déplacement de “./public/index.php” vers “./p/i/index.php” (voir cookie ci-dessous) diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 81dfefabb..772a08f30 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -47,8 +47,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->_useLayout (false); header('Content-Type: application/rss+xml; charset=utf-8'); } else { - Minz_View::appendScript (Minz_Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); - if ($output === 'global') { Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); } @@ -290,8 +288,56 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function logoutAction () { + $this->view->_useLayout(false); + invalidateHttpCache(); + Minz_Session::_param('currentUser'); + Minz_Session::_param('mail'); + Minz_Session::_param('passwordHash'); + } + + public function formLoginAction () { $this->view->_useLayout (false); - Minz_Session::_param ('mail'); + if (Minz_Request::isPost()) { + $ok = false; + $nonce = Minz_Session::param('nonce'); + $username = Minz_Request::param('username', ''); + $c = Minz_Request::param('challenge', ''); + if (ctype_alnum($username) && ctype_graph($c) && ctype_alnum($nonce)) { + if (!function_exists('password_verify')) { + include_once(LIB_PATH . '/password_compat.php'); + } + try { + $conf = new FreshRSS_Configuration($username); + $s = $conf->passwordHash; + $ok = password_verify($nonce . $s, $c); + if ($ok) { + Minz_Session::_param('currentUser', $username); + Minz_Session::_param('passwordHash', $s); + } else { + Minz_Log::record('Password mismatch for user ' . $username . ', nonce=' . $nonce . ', c=' . $c, Minz_Log::WARNING); + } + } catch (Minz_Exception $me) { + Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING); + } + } + if (!$ok) { + $notif = array( + 'type' => 'bad', + 'content' => Minz_Translate::t('invalid_login') + ); + Minz_Session::_param('notification', $notif); + } + } + invalidateHttpCache(); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); + } + + public function formLogoutAction () { + $this->view->_useLayout(false); invalidateHttpCache(); + Minz_Session::_param('currentUser'); + Minz_Session::_param('mail'); + Minz_Session::_param('passwordHash'); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } } diff --git a/app/Controllers/javascriptController.php b/app/Controllers/javascriptController.php index e29f439d8..02e424437 100755 --- a/app/Controllers/javascriptController.php +++ b/app/Controllers/javascriptController.php @@ -17,7 +17,7 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { $this->view->categories = $catDAO->listCategories(true, false); } - // For Web-form login + //For Web-form login public function nonceAction() { header('Content-Type: application/json; charset=UTF-8'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T')); @@ -29,15 +29,15 @@ class FreshRSS_javascript_Controller extends Minz_ActionController { if (ctype_alnum($user)) { try { $conf = new FreshRSS_Configuration($user); - $hash = $conf->passwordHash; //CRYPT_BLOWFISH - Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". - if (strlen($hash) >= 60) { - $this->view->salt1 = substr($hash, 0, 29); + $s = $conf->passwordHash; + if (strlen($s) >= 60) { + $this->view->salt1 = substr($s, 0, 29); //CRYPT_BLOWFISH Salt: "$2a$", a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z". $this->view->nonce = sha1(Minz_Configuration::salt() . uniqid(mt_rand(), true)); - Minz_Session::_param ('nonce', $this->view->nonce); + Minz_Session::_param('nonce', $this->view->nonce); return; //Success } } catch (Minz_Exception $me) { - Minz_Log::record ('Login failure: ' . $me->getMessage(), Minz_Log::WARNING); + Minz_Log::record('Login failure: ' . $me->getMessage(), Minz_Log::WARNING); } } $this->view->nonce = ''; //Failure diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 8954c845d..cb5ebd209 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -21,18 +21,20 @@ class FreshRSS_users_Controller extends Minz_ActionController { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT); //A bit expensive, on purpose + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => 8)); //This will also have to be computed client side on mobile devices, so do not use a too high cost $passwordPlain = ''; + $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js $this->view->conf->_passwordHash($passwordHash); } - $mail = Minz_Request::param('mail_login', false); - $this->view->conf->_mail_login($mail); + $email = Minz_Request::param('mail_login', false); + $this->view->conf->_mail_login($email); $ok &= $this->view->conf->save(); $email = $this->view->conf->mail_login; Minz_Session::_param('mail', $email); + Minz_Session::_param('passwordHash', $this->view->conf->passwordHash); if ($email != '') { $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; @@ -89,10 +91,25 @@ class FreshRSS_users_Controller extends Minz_ActionController { $ok &= !file_exists($configPath); } if ($ok) { + + $passwordPlain = Minz_Request::param('new_user_passwordPlain', false); + $passwordHash = ''; + if ($passwordPlain != '') { + Minz_Request::_param('new_user_passwordPlain'); //Discard plain-text password ASAP + $_POST['new_user_passwordPlain'] = ''; + if (!function_exists('password_hash')) { + include_once(LIB_PATH . '/password_compat.php'); + } + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => 8)); + $passwordPlain = ''; + } + if (empty($passwordHash)) { + $passwordHash = ''; + } + $new_user_email = filter_var($_POST['new_user_email'], FILTER_VALIDATE_EMAIL); if (empty($new_user_email)) { $new_user_email = ''; - $ok &= Minz_Configuration::authType() !== 'persona'; } else { $personaFile = DATA_PATH . '/persona/' . $new_user_email . '.txt'; @unlink($personaFile); @@ -102,6 +119,7 @@ class FreshRSS_users_Controller extends Minz_ActionController { if ($ok) { $config_array = array( 'language' => $new_user_language, + 'passwordHash' => $passwordHash, 'mail_login' => $new_user_email, ); $ok &= (file_put_contents($configPath, "accessControl(Minz_Session::param('currentUser', '')); + $loginOk = $this->accessControl(Minz_Session::param('currentUser', '')); $this->loadParamsView(); - $this->loadStylesAndScripts(); //TODO: Do not load that when not needed, e.g. some Ajax requests + $this->loadStylesAndScripts($loginOk); //TODO: Do not load that when not needed, e.g. some Ajax requests $this->loadNotifications(); } private function accessControl($currentUser) { if ($currentUser == '') { switch (Minz_Configuration::authType()) { + case 'form': + $currentUser = Minz_Configuration::defaultUser(); + Minz_Session::_param('passwordHash'); + $loginOk = false; + break; case 'http_auth': $currentUser = httpAuthUser(); $loginOk = $currentUser != ''; @@ -73,6 +78,9 @@ class FreshRSS extends Minz_FrontController { if ($loginOk) { switch (Minz_Configuration::authType()) { + case 'form': + $loginOk = Minz_Session::param('passwordHash') === $this->conf->passwordHash; + break; case 'http_auth': $loginOk = strcasecmp($currentUser, httpAuthUser()) === 0; break; @@ -92,6 +100,7 @@ class FreshRSS extends Minz_FrontController { } } Minz_View::_param ('loginOk', $loginOk); + return $loginOk; } private function loadParamsView () { @@ -104,7 +113,7 @@ class FreshRSS extends Minz_FrontController { } } - private function loadStylesAndScripts () { + private function loadStylesAndScripts ($loginOk) { $theme = FreshRSS_Themes::get_infos($this->conf->theme); if ($theme) { foreach($theme['files'] as $file) { @@ -112,14 +121,22 @@ class FreshRSS extends Minz_FrontController { } } - if (Minz_Configuration::authType() === 'persona') { - Minz_View::appendScript ('https://login.persona.org/include.js'); + switch (Minz_Configuration::authType()) { + case 'form': + if (!$loginOk) { + Minz_View::appendScript(Minz_Url::display ('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); + } + break; + case 'persona': + Minz_View::appendScript('https://login.persona.org/include.js'); + break; } $includeLazyLoad = $this->conf->lazyload && ($this->conf->display_posts || Minz_Request::param ('output') === 'reader'); Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.min.js')), false, !$includeLazyLoad, !$includeLazyLoad); if ($includeLazyLoad) { Minz_View::appendScript (Minz_Url::display ('/scripts/jquery.lazyload.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.lazyload.min.js'))); } + Minz_View::appendScript (Minz_Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js'))); Minz_View::appendScript (Minz_Url::display ('/scripts/main.js?' . @filemtime(PUBLIC_PATH . '/scripts/main.js'))); } diff --git a/app/i18n/en.php b/app/i18n/en.php index 3b9936e8e..71ca9538f 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -170,6 +170,9 @@ return array ( 'is_admin' => 'is administrator', 'auth_type' => 'Authentication method', 'auth_none' => 'None (dangerous)', + 'auth_form' => 'Web form (traditional, requires JavaScript)', + 'http_auth' => 'HTTP (for advanced users with HTTPS)', + 'auth_persona' => 'Mozilla Persona (modern, requires JavaScript)', 'users_list' => 'List of users', 'create_user' => 'Create new user', 'username' => 'Username', @@ -258,8 +261,8 @@ return array ( 'logs_empty' => 'Log file is empty', 'clear_logs' => 'Clear the logs', - 'forbidden_access' => 'Forbidden access', - 'forbidden_access_description' => 'Access is password protected, please to read your feeds.', + 'forbidden_access' => 'Access forbidden! (%s)', + 'login_required' => 'Login required:', 'confirm_action' => 'Are you sure you want to perform this action? It cannot be cancelled!', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 7e71cbb6d..8ffc5ec88 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -170,6 +170,9 @@ return array ( 'is_admin' => 'est administrateur', 'auth_type' => 'Méthode d’authentification', 'auth_none' => 'Aucune (dangereux)', + 'auth_form' => 'Formulaire (traditionnel, requiert JavaScript)', + 'http_auth' => 'HTTP (pour utilisateurs avancés avec HTTPS)', + 'auth_persona' => 'Mozilla Persona (moderne, requiert JavaScript)', 'users_list' => 'Liste des utilisateurs', 'create_user' => 'Créer un nouvel utilisateur', 'username' => 'Nom d’utilisateur', @@ -258,8 +261,8 @@ return array ( 'logs_empty' => 'Les logs sont vides', 'clear_logs' => 'Effacer les logs', - 'forbidden_access' => 'Accès interdit', - 'forbidden_access_description' => 'L’accès est protégé par un mot de passe, veuillez pour accéder aux flux.', + 'forbidden_access' => 'Accès interdit ! (%s)', + 'login_required' => 'Accès protégé par mot de passe :', 'confirm_action' => 'Êtes-vous sûr(e) de vouloir continuer ? Cette action ne peut être annulée !', diff --git a/app/layout/header.phtml b/app/layout/header.phtml index 0f2c524c4..e90da6196 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -1,12 +1,25 @@ - - - +
    @@ -62,16 +75,24 @@
  • - -
  • -
  • - +
  • - +
    - +
    diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 68111bdbe..1597004e1 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -20,7 +20,7 @@
    - +
    @@ -52,11 +52,11 @@
    - $_SERVER['REMOTE_USER'] = ``
    @@ -141,6 +141,14 @@ +
    + +
    + + +
    +
    +
    conf->mail_login; ?> diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 92c068f7e..935294e60 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -30,8 +30,8 @@ if ($mail != 'null') { $mail = '"' . $mail . '"'; } - echo 'use_persona=', Minz_Configuration::authType() === 'persona' ? 'true' : 'false', - ',url_freshrss="', _url ('index', 'index'), '",', + echo 'authType="', Minz_Configuration::authType(), '",', + 'url_freshrss="', _url ('index', 'index'), '",', 'url_login="', _url ('index', 'login'), '",', 'url_logout="', _url ('index', 'logout'), '",', 'current_user_mail=', $mail, ",\n"; diff --git a/app/views/helpers/view/login.phtml b/app/views/helpers/view/login.phtml new file mode 100644 index 000000000..e4a24f9ed --- /dev/null +++ b/app/views/helpers/view/login.phtml @@ -0,0 +1,43 @@ +
    + +

    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +

    FreshRSS

    +

    + +

    +
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 549d0b61e..9b69233e9 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -1,15 +1,5 @@
    -

    -

    -

    -
    loginOk || Minz_Configuration::allowAnonymous()) { @@ -31,8 +21,8 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) { if ($token_is_ok) { $this->renderHelper ('view/rss_view'); } else { - showForbidden(); + $this->renderHelper ('view/login'); } } else { - showForbidden(); + $this->renderHelper ('view/login'); } diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 2c30661ed..433992e0d 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -109,7 +109,7 @@ class Minz_Configuration { return self::$auth_type !== 'none'; } public static function canLogIn() { - return self::$auth_type === 'persona'; + return self::$auth_type === 'form' || self::$auth_type === 'persona'; } public static function _allowAnonymous($allow = false) { @@ -118,6 +118,7 @@ class Minz_Configuration { public static function _authType($value) { $value = strtolower($value); switch ($value) { + case 'form': case 'http_auth': case 'persona': case 'none': diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 7b8526bc8..80eda8877 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -34,7 +34,7 @@ class Minz_FrontController { */ public function __construct () { if (LOG_PATH === false) { - $this->killApp ('Path doesn’t exist : LOG_PATH'); + $this->killApp ('Path not found: LOG_PATH'); } try { diff --git a/p/scripts/main.js b/p/scripts/main.js index 24af1b210..0c4c3f1b2 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -587,6 +587,54 @@ function init_load_more(box) { } // +// +function poormanSalt() { //If crypto.getRandomValues is not available + var text = '$2a$04$', + base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz'; + for (var i = 22; i > 0; i--) { + text += base.charAt(Math.floor(Math.random() * 64)); + } + return text; +} + +function init_loginForm() { + var $loginForm = $('#loginForm'); + if ($loginForm.length === 0) { + return; + } + if (!(window.dcodeIO)) { + if (window.console) { + console.log('FreshRSS waiting for bcrypt.js…'); + } + window.setTimeout(init_loginForm, 100); + return; + } + $loginForm.on('submit', function() { + $('#loginButton').attr('disabled', ''); + var success = false; + $.ajax({ + url: './?c=javascript&a=nonce&user=' + $('#username').val(), + dataType: 'json', + async: false + }).done(function (data) { + if (data.salt1 == '' || data.nonce == '') { + alert('Invalid user!'); + } else { + var strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'), + s = dcodeIO.bcrypt.hashSync($('#passwordPlain').val(), data.salt1), + c = dcodeIO.bcrypt.hashSync(data.nonce + s, strong ? 4 : poormanSalt()); + $('#challenge').val(c); + success = true; + } + }).fail(function() { + alert('Communication error!'); + }); + $('#loginButton').removeAttr('disabled'); + return success; + }); +} +// + // function init_persona() { if (!(navigator.id)) { @@ -696,8 +744,13 @@ function init_all() { init_notifications(); init_actualize(); init_load_more($stream); - if (use_persona) { - init_persona(); + switch (authType) { + case 'form': + init_loginForm(); + break; + case 'persona': + init_persona(); + break; } init_confirm_action(); init_print_action(); -- cgit v1.2.3 From 0f481f7f24dfad3bf9775213f487dd6802b6cb6a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 12 Jan 2014 14:00:02 +0100 Subject: Permet aux utilisations non-administrateurs de changer leur mot de passe https://github.com/marienfressinaud/FreshRSS/issues/104 --- app/Controllers/usersController.php | 21 +++++++++++++-------- app/views/configure/users.phtml | 2 -- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index cb5ebd209..7e44b3d35 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -1,6 +1,9 @@ view->loginOk) { Minz_Error::error( @@ -21,20 +24,21 @@ class FreshRSS_users_Controller extends Minz_ActionController { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => 8)); //This will also have to be computed client side on mobile devices, so do not use a too high cost + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); $passwordPlain = ''; $passwordHash = preg_replace('/^\$2[xy]\$/', '\$2a\$', $passwordHash); //Compatibility with bcrypt.js + $ok &= ($passwordHash != ''); $this->view->conf->_passwordHash($passwordHash); } + Minz_Session::_param('passwordHash', $this->view->conf->passwordHash); - $email = Minz_Request::param('mail_login', false); - $this->view->conf->_mail_login($email); - - $ok &= $this->view->conf->save(); - + if (Minz_Configuration::isAdmin(Minz_Session::param('currentUser', '_'))) { + $this->view->conf->_mail_login(Minz_Request::param('mail_login', false)); + } $email = $this->view->conf->mail_login; Minz_Session::_param('mail', $email); - Minz_Session::_param('passwordHash', $this->view->conf->passwordHash); + + $ok &= $this->view->conf->save(); if ($email != '') { $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; @@ -100,8 +104,9 @@ class FreshRSS_users_Controller extends Minz_ActionController { if (!function_exists('password_hash')) { include_once(LIB_PATH . '/password_compat.php'); } - $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => 8)); + $passwordHash = password_hash($passwordPlain, PASSWORD_BCRYPT, array('cost' => self::BCRYPT_COST)); $passwordPlain = ''; + $ok &= ($passwordHash != ''); } if (empty($passwordHash)) { $passwordHash = ''; diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 1597004e1..0419df747 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -34,14 +34,12 @@
    -
    - -- cgit v1.2.3 From f720d41cbfb18edc1b0a694a7213682b96befa1f Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 13 Jan 2014 22:27:22 +0100 Subject: Mise à jour nom thèmes + chargement robuste des thèmes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renomme : default -> Origine En cas de thème introuvable, charge le thème par défaut, sinon le premier disponible. https://github.com/marienfressinaud/FreshRSS/issues/120 --- app/Controllers/configureController.php | 6 +- app/FreshRSS.php | 5 +- app/Models/Configuration.php | 2 +- app/Models/Themes.php | 28 +- app/views/configure/display.phtml | 5 +- p/themes/Dark/freshrss.css | 862 ++++++++++++++++++++++++++++++ p/themes/Dark/global.css | 487 +++++++++++++++++ p/themes/Dark/loader.gif | Bin 0 -> 404 bytes p/themes/Dark/metadata.json | 7 + p/themes/Flat/freshrss.css | 817 ++++++++++++++++++++++++++++ p/themes/Flat/global.css | 495 +++++++++++++++++ p/themes/Flat/icons/add.svg | 30 ++ p/themes/Flat/icons/all.svg | 32 ++ p/themes/Flat/icons/close.svg | 28 + p/themes/Flat/icons/configure.svg | 31 ++ p/themes/Flat/icons/down.svg | 31 ++ p/themes/Flat/icons/next.svg | 31 ++ p/themes/Flat/icons/prev.svg | 31 ++ p/themes/Flat/icons/refresh.svg | 31 ++ p/themes/Flat/icons/search.svg | 32 ++ p/themes/Flat/icons/up.svg | 31 ++ p/themes/Flat/loader.gif | Bin 0 -> 4251 bytes p/themes/Flat/metadata.json | 7 + p/themes/Origine/freshrss.css | 880 +++++++++++++++++++++++++++++++ p/themes/Origine/global.css | 503 ++++++++++++++++++ p/themes/Origine/loader.gif | Bin 0 -> 4167 bytes p/themes/Origine/metadata.json | 7 + p/themes/default/freshrss.css | 880 ------------------------------- p/themes/default/global.css | 503 ------------------ p/themes/default/loader.gif | Bin 4167 -> 0 bytes p/themes/default/metadata.json | 7 - p/themes/default_dark/freshrss.css | 862 ------------------------------ p/themes/default_dark/global.css | 487 ----------------- p/themes/default_dark/loader.gif | Bin 404 -> 0 bytes p/themes/default_dark/metadata.json | 7 - p/themes/flat-design/freshrss.css | 817 ---------------------------- p/themes/flat-design/global.css | 495 ----------------- p/themes/flat-design/icons/add.svg | 30 -- p/themes/flat-design/icons/all.svg | 32 -- p/themes/flat-design/icons/close.svg | 28 - p/themes/flat-design/icons/configure.svg | 31 -- p/themes/flat-design/icons/down.svg | 31 -- p/themes/flat-design/icons/next.svg | 31 -- p/themes/flat-design/icons/prev.svg | 31 -- p/themes/flat-design/icons/refresh.svg | 31 -- p/themes/flat-design/icons/search.svg | 32 -- p/themes/flat-design/icons/up.svg | 31 -- p/themes/flat-design/loader.gif | Bin 4251 -> 0 bytes p/themes/flat-design/metadata.json | 7 - 49 files changed, 4407 insertions(+), 4385 deletions(-) create mode 100644 p/themes/Dark/freshrss.css create mode 100644 p/themes/Dark/global.css create mode 100644 p/themes/Dark/loader.gif create mode 100644 p/themes/Dark/metadata.json create mode 100644 p/themes/Flat/freshrss.css create mode 100644 p/themes/Flat/global.css create mode 100644 p/themes/Flat/icons/add.svg create mode 100644 p/themes/Flat/icons/all.svg create mode 100644 p/themes/Flat/icons/close.svg create mode 100644 p/themes/Flat/icons/configure.svg create mode 100644 p/themes/Flat/icons/down.svg create mode 100644 p/themes/Flat/icons/next.svg create mode 100644 p/themes/Flat/icons/prev.svg create mode 100644 p/themes/Flat/icons/refresh.svg create mode 100644 p/themes/Flat/icons/search.svg create mode 100644 p/themes/Flat/icons/up.svg create mode 100644 p/themes/Flat/loader.gif create mode 100644 p/themes/Flat/metadata.json create mode 100644 p/themes/Origine/freshrss.css create mode 100644 p/themes/Origine/global.css create mode 100644 p/themes/Origine/loader.gif create mode 100644 p/themes/Origine/metadata.json delete mode 100644 p/themes/default/freshrss.css delete mode 100644 p/themes/default/global.css delete mode 100644 p/themes/default/loader.gif delete mode 100644 p/themes/default/metadata.json delete mode 100644 p/themes/default_dark/freshrss.css delete mode 100644 p/themes/default_dark/global.css delete mode 100644 p/themes/default_dark/loader.gif delete mode 100644 p/themes/default_dark/metadata.json delete mode 100644 p/themes/flat-design/freshrss.css delete mode 100644 p/themes/flat-design/global.css delete mode 100644 p/themes/flat-design/icons/add.svg delete mode 100644 p/themes/flat-design/icons/all.svg delete mode 100644 p/themes/flat-design/icons/close.svg delete mode 100644 p/themes/flat-design/icons/configure.svg delete mode 100644 p/themes/flat-design/icons/down.svg delete mode 100644 p/themes/flat-design/icons/next.svg delete mode 100644 p/themes/flat-design/icons/prev.svg delete mode 100644 p/themes/flat-design/icons/refresh.svg delete mode 100644 p/themes/flat-design/icons/search.svg delete mode 100644 p/themes/flat-design/icons/up.svg delete mode 100644 p/themes/flat-design/loader.gif delete mode 100644 p/themes/flat-design/metadata.json (limited to 'app/Controllers') diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 5b5770cbe..70144a8db 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -157,7 +157,11 @@ class FreshRSS_configure_Controller extends Minz_ActionController { 'scroll' => Minz_Request::param('mark_scroll', false), 'reception' => Minz_Request::param('mark_upon_reception', false), )); - $this->view->conf->_theme(Minz_Request::param('theme', 'default')); + $themeId = Minz_Request::param('theme', ''); + if ($themeId == '') { + $themeId = FreshRSS_Themes::defaultTheme; + } + $this->view->conf->_theme($themeId); $this->view->conf->_topline_read(Minz_Request::param('topline_read', false)); $this->view->conf->_topline_favorite(Minz_Request::param('topline_favorite', false)); $this->view->conf->_topline_date(Minz_Request::param('topline_date', false)); diff --git a/app/FreshRSS.php b/app/FreshRSS.php index e64a1986b..52f34c6e2 100644 --- a/app/FreshRSS.php +++ b/app/FreshRSS.php @@ -114,11 +114,10 @@ class FreshRSS extends Minz_FrontController { } private function loadStylesAndScripts ($loginOk) { - $theme = FreshRSS_Themes::get_infos($this->conf->theme); + $theme = FreshRSS_Themes::load($this->conf->theme); if ($theme) { - FreshRSS_Themes::setThemeId($this->conf->theme); foreach($theme['files'] as $file) { - Minz_View::appendStyle (Minz_Url::display ('/themes/' . $theme['path'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['path'] . '/' . $file))); + Minz_View::appendStyle (Minz_Url::display ('/themes/' . $theme['id'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['path'] . '/' . $file))); } } diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index 8f394737a..f3fb76e72 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -25,7 +25,7 @@ class FreshRSS_Configuration { 'scroll' => false, 'reception' => false, ), - 'theme' => 'default', + 'theme' => 'Origine', 'shortcuts' => array( 'mark_read' => 'r', 'mark_favorite' => 'f', diff --git a/app/Models/Themes.php b/app/Models/Themes.php index a52812339..c7099a1df 100644 --- a/app/Models/Themes.php +++ b/app/Models/Themes.php @@ -3,13 +3,17 @@ class FreshRSS_Themes extends Minz_Model { private static $themesUrl = '/themes/'; private static $defaultIconsUrl = '/themes/icons/'; + public static $defaultTheme = 'Origine'; - public static function get() { - $themes_list = array_diff( + public static function getList() { + return array_values(array_diff( scandir(PUBLIC_PATH . self::$themesUrl), array('..', '.') - ); + )); + } + public static function get() { + $themes_list = self::getList(); $list = array(); foreach ($themes_list as $theme_dir) { $theme = self::get_infos($theme_dir); @@ -28,7 +32,7 @@ class FreshRSS_Themes extends Minz_Model { $content = file_get_contents($json_filename); $res = json_decode($content, true); if ($res && isset($res['files']) && is_array($res['files'])) { - $res['path'] = $theme_id; + $res['id'] = $theme_id; return $res; } } @@ -39,12 +43,26 @@ class FreshRSS_Themes extends Minz_Model { private static $themeIconsUrl; private static $themeIcons; - public static function setThemeId($theme_id) { + public static function load($theme_id) { + $infos = self::get_infos($theme_id); + if (!$infos) { + if ($theme_id !== self::$defaultTheme) { //Fall-back to default theme + return self::load(self::$defaultTheme); + } + $themes_list = self::getList(); + if (!empty($themes_list)) { + if ($theme_id !== $themes_list[0]) { //Fall-back to first theme + return self::load($themes_list[0]); + } + } + return false; + } self::$themeIconsUrl = self::$themesUrl . $theme_id . '/icons/'; self::$themeIcons = is_dir(PUBLIC_PATH . self::$themeIconsUrl) ? array_fill_keys(array_diff( scandir(PUBLIC_PATH . self::$themeIconsUrl), array('..', '.') ), 1) : array(); + return $infos; } public static function icon($name, $urlOnly = false) { diff --git a/app/views/configure/display.phtml b/app/views/configure/display.phtml index 3cc5442fb..725356c8d 100644 --- a/app/views/configure/display.phtml +++ b/app/views/configure/display.phtml @@ -21,9 +21,10 @@
    - + themes as $theme) { ?> - diff --git a/p/themes/Dark/freshrss.css b/p/themes/Dark/freshrss.css new file mode 100644 index 000000000..e9eb2c705 --- /dev/null +++ b/p/themes/Dark/freshrss.css @@ -0,0 +1,862 @@ +@charset "UTF-8"; + +/* STRUCTURE */ +.header { + display: table; + width: 100%; + background: #1c1c1c; + table-layout: fixed; +} + .header > .item { + display: table-cell; + padding: 10px 0; + border-bottom: 1px solid #2f2f2f; + vertical-align: middle; + text-align: center; + } + .header > .item.title { + width: 250px; + white-space: nowrap; + } + .logo { + display: inline-block; + font-size: 48px; + height: 32px; + width: 32px; + padding: 10px; + } + .header > .item.title h1 { + display: inline-block; + margin: 0; + } + .header > .item.search input { + width: 230px; + transition: width 200ms linear; + } + .header .item.search input:focus { + width: 330px; + } + .header > .item.configure { + width: 100px; + } + +.item a:hover { + text-decoration: none; +} + +#global { + display: table; + width: 100%; + height: 100%; + background: #1c1c1c; + table-layout: fixed; +} + .aside { + display: table-cell; + height: 100%; + width: 250px; + vertical-align: top; + border-right: 1px solid #2f2f2f; + background: #1c1c1c; + } + .aside .nav-form input { + width: 180px; + } + .aside.aside_flux { + padding: 10px 0 40px; + } + .aside.aside_feed .nav-form input { + width: 140px; + } + .aside.aside_feed .nav-form .dropdown .dropdown-menu { + right: -20px; + } + .aside.aside_feed .nav-form .dropdown .dropdown-menu:after { + right: 33px; + } + + .nav-login { + display: none; + } + + .nav_menu { + width: 100%; + background: #1c1c1c; + border-bottom: 1px solid #2f2f2f; + text-align: center; + padding: 5px 0; + } + .nav_menu .search { + display:none; + } + +.favicon { + height: 16px; + width: 16px; +} + +.categories { + margin: 0; + padding: 0; + text-align: center; + list-style: none; +} + .category { + display: block; + width: 220px; + margin: 10px auto; + text-align: left; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .category .btn:first-child { + width: 195px; + position: relative; + } + .category.stick .btn:first-child { + width:160px; + } + .category .btn:first-child:not([data-unread="0"]):after { + content: attr(data-unread); + position: absolute; + top: 3px; right: 3px; + padding: 1px 5px; + background: #1a1a1a; + color: #888; + font-size: 90%; + border: 1px solid #000; + border-radius: 5px; + } + .category + .feeds:not(.active) { + display:none; + } + .categories .feeds { + width: 100%; + margin: 0; + list-style: none; + } + .categories .feeds .item.active { + background: #26303F; + } + .categories .feeds .item.active .feed { + color: #888; + } + .categories .feeds .item.empty .feed { + color: #e67e22; + } + .categories .feeds .item.empty.active { + background: #e67e22; + } + .categories .feeds .item.empty.active .feed { + color: #fff; + } + .categories .feeds .item.error .feed { + color: #BD362F; + } + .categories .feeds .item .feed { + display: inline-block; + margin: 0; + width: 165px; + line-height: 35px; + font-size: 90%; + vertical-align: middle; + text-align: left; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .feed:not([data-unread="0"]):before { + content: "(" attr(data-unread) ") "; + } + .categories .feeds .dropdown-menu { + left: 0; + } + .categories .feeds .dropdown-menu:after { + left: 2px; + } + .categories .feeds .item .dropdown-toggle > .icon { + visibility: hidden; + cursor: pointer; + } + .categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, + .categories .feeds .item:hover .dropdown-toggle > .icon, + .categories .feeds .item.active .dropdown-toggle > .icon { + background-color: #1c1c1c; + border-radius: 3px; + visibility: visible; + } + +.post { + padding: 10px 50px; +} + .post form { + margin: 10px 0; + } + +.day { + min-height: 50px; + padding: 0 10px; + font-size: 130%; + font-weight: bold; + line-height: 50px; + background: #1c1c1c; + border-top: 1px solid #888; + border-bottom: 1px solid #888; +} + .day:first-child { + border-top: none; + } + .day .name { + position: absolute; + right: 0; + width: 50%; + height: 1.5em; + padding: 0 10px 0 0; + overflow: hidden; + color: #aab; + font-size: 1.8em; + opacity: .3; + text-shadow: 0px -1px 0px #333; + font-style: italic; + white-space: nowrap; + text-overflow: ellipsis; + text-align: right; + } + +.flux { + border-left: 3px solid #aaa; + background: #1c1c1c; +} + .flux.not_read { + border-left: 3px solid #FF5300; + background: #1c1c1c; + } + .flux.favorite { + border-left: 3px solid #FFC300; + background: #1c1c1c; + } + .flux.current { + border-left: 3px solid #0062BE; + background: #1a1a1a; + } + + .flux_header { + background: inherit; + height: 25px; + font-size: 12px; + border-top: 1px solid #2f2f2f; + cursor: pointer; + } + .flux .item { + line-height: 40px; + white-space: nowrap; + } + .flux_header > .item { + overflow: hidden; + text-overflow: ellipsis; + } + .flux .item.manage { + width: 40px; + text-align: center; + } + .flux .item.website { + width: 200px; + } + .website .favicon { + padding: 5px; + } + .flux .item.title { + background: inherit; + } + .flux:hover .item.title { + border-right: 2px solid rgba(127, 127, 127, 0.1); + padding-right: 1em; + position: absolute; + } + .flux .item.title a { + color: #888; + outline: none; + } + .flux.not_read .item.title, + .flux.current .item.title { + font-weight: bold; + } + .flux .item.date { + width: 200px; + padding:0 5px 0 0; + text-align: right; + font-size: 10px; + color: #666; + } + .link { + width: 40px; + text-align: center; + } + +#stream.reader .flux { + padding: 0 0 30px; + border: none; + background: #1c1c1c; + color: #888; +} + #stream.reader .flux .author { + margin: 0 0 10px; + font-size: 90%; + color: #666; + } + +#stream.global { + text-align: center; +} + #stream.global .box-category { + display: inline-block; + width: 280px; + margin: 20px 10px; + vertical-align: top; + background: #1a1a1a; + border: 1px solid #000; + border-radius: 5px; + text-align: left; + box-shadow: 0 0 5px #2f2f2f; + } + #stream.global .category { + width: 100%; + margin: 0; + } + #stream.global .btn { + display: block; + width: auto; + height: 35px; + margin: 0; + padding: 0 10px; + background: #26303F; + border: none; + border-bottom: 1px solid #2f2f2f; + border-radius: 5px 5px 0 0; + line-height: 35px; + font-size: 120%; + } + #stream.global .btn:not([data-unread="0"]) { + font-weight:bold; + } + #stream.global .btn:first-child:not([data-unread="0"]):after { + top: 0; right: 5px; + border: 0; + background: none; + color: #666; + font-weight: bold; + box-shadow: none; + } + #stream.global .box-category .feeds { + display: block; + max-height: 250px; + margin: 0; + list-style: none; + overflow: auto; + } + #stream.global .box-category .feeds .item { + padding: 2px 10px; + font-size: 90%; + } + #stream.global .box-category .feed { + width: 220px; + } + +.content { + min-height: 150px; + max-width: 550px; + margin: 0 auto; + padding: 20px 10px; + line-height: 170%; + word-wrap: break-word; +} + .content h1, .content h2, .content h3 { + margin: 20px 0 5px; + } + .content > .title { + font-size: x-large; + margin: 0; + } + + .content p { + margin: 0 0 20px; + } + img.big { + display: block; + margin: 10px auto; + } + figure img.big { + margin: 0; + } + .content hr { + margin: 30px 0; + height: 1px; + background: #ddd; + border: 0; + } + .content pre { + margin: 10px auto; + padding: 10px; + overflow: auto; + background: #000; + color: #fff; + font-size: 110%; + } + .content q, .content blockquote { + display: block; + margin: 5px 0; + padding: 5px 20px; + font-style: italic; + border-left: 4px solid #ccc; + color: #666; + } + .content blockquote p { + margin: 0; + } + +#panel { + display: none; + position: fixed; + top: 10px; bottom: 10px; + left: 100px; right: 100px; + overflow: auto; + background: #1c1c1c; + border: 1px solid #95a5a6; + border-radius: 5px; +} + #panel .close { + position: fixed; + top: 10px; right: 0; + display: inline-block; + width: 26px; + height: 26px; + margin: 0 10px 0 0; + border: 1px solid #ccc; + border-radius: 20px; + text-align: center; + line-height: 26px; + background: #fff; + } + +#overlay { + display: none; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + background: rgba(0, 0, 0, 0.9); +} + +.flux_content .bottom { + font-size: 90%; + text-align: center; +} + +.hide_posts > :not(.active) > .flux_content { + display:none; +} + +/*** PAGINATION ***/ +.pagination { + display: table; + width: 100%; + margin: 0; + background: #1a1a1a; + text-align: center; + color: #888; + font-size: 80%; + line-height: 200%; + table-layout: fixed; +} + .pagination .item { + display: table-cell; + line-height: 40px; + } + .pagination .item.pager-current { + font-weight: bold; + font-size: 140%; + } + .pagination .pager-first, + .pagination .pager-previous, + .pagination .pager-next, + .pagination .pager-last { + width: 100px; + } + .pagination .item a { + display: block; + color: #333; + font-style: italic; + } + .pagination:first-child .item { + border-bottom: 1px solid #aaa; + } + .pagination:last-child .item { + border-top: 1px solid #aaa; + } + +#nav_entries { + display: table; + width: 250px; + height: 40px; + position: fixed; + bottom: 0; + left: 0; + margin: 0; + background: #1c1c1c; + border-top: 1px solid #2f2f2f; + text-align: center; + line-height: 40px; + table-layout: fixed; +} + #nav_entries .item { + display: table-cell; + width: 30%; + } + #nav_entries a { + display: block; + } + #nav_entries .i_up { + margin: 5px 0 0; + vertical-align: top; + } + +.loading { + background: url("loader.gif") center center no-repeat; + font-size: 0; +} + +#bigMarkAsRead { + display: block; + font-style: normal; + padding: 32px 0 64px 0; + text-align: center; + text-decoration: none; +} + #bigMarkAsRead:hover { + background: #1c1c1c; + color: #888; + } + .bigTick { + font-size: 72pt; + line-height: 1.6em; + } + +/*** NOTIFICATION ***/ +.notification { + position: absolute; + top: 10px; + left: 25%; right: 25%; + min-height: 30px; + padding: 10px; + line-height: 30px; + text-align: center; + border-radius: 5px; + box-shadow: 0 0 5px #666; + background: #1a1a1a; + color: #888; + font-weight: bold; + z-index: 10; +} + .notification.good { + border:1px solid #f4f899; + } + .notification.bad { + border:1px solid #f4a899; + } + .notification a.close { + display: inline-block; + width: 16px; + height: 16px; + float: right; + margin: -20px -20px 0 0; + padding: 5px; + background: #1a1a1a; + border-radius: 50px; + line-height: 16px; + } + .notification.good a.close{ + border:1px solid #f4f899; + } + .notification.bad a.close{ + border:1px solid #f4a899; + } + +.toggle_aside, .btn.toggle_aside { + display: none; +} + +.actualizeProgress { + position: fixed; + top: 10px; + left: 25%; right: 25%; + padding: 5px; + background: #1a1a1a; + text-align: center; + border: 1px solid #2f2f2f; + border-radius: 5px; +} + .actualizeProgress progress { + max-width: 100%; + vertical-align: middle; + } + .actualizeProgress .progress { + color: #888; + font-size: 90%; + vertical-align: middle; + } + +.logs { + border: 1px solid #aaa; +} + .log { + padding: 5px 2%; + overflow: auto; + background: #fafafa; + border-bottom: 1px solid #999; + color: #333; + font-size: 90%; + } + .log .date { + display: block; + } + .log.error { + background: #fdd; + color: #844; + } + .log.warning { + background: #ffe; + color: #c95; + } + .log.notice { + background: #f4f4f4; + color: #aaa; + } + .log.debug { + background: #111; + color: #eee; + } + +.form-group table { + border-collapse:collapse; + margin:10px 0 0 220px; + text-align:center; +} + +.form-group tr, .form-group th, .form-group td { + border:1px solid #2f2f2f; + font-weight:normal; + padding:.5em; +} + +select.number option { + text-align:right; +} + +@media(max-width: 840px) { + .header, + .aside .btn-important, + .aside .feeds .dropdown, + .flux_header .item.website span, + .item.date { + display: none; + } + .flux_header .item.website { + width: 40px; + text-align: center; + } + .flux_header .item.website .favicon { + padding: 12px; + } + + .nav-login { + display: block; + } + + .content { + font-size: 120%; + padding: 0; + } + + .pagination { + margin: 0 0 40px; + } + .pagination .pager-previous, .pagination .pager-next { + width: 100px; + } + + .toggle_aside, .btn.toggle_aside { + display: inline-block; + } + .aside { + position: fixed; + top: 0; left: 0; + width: 0; + overflow: hidden; + border-right: none; + z-index: 10; + transition: width 200ms linear; + } + .aside.aside_flux { + padding: 10px 0 0; + } + .aside:target { + width: 80%; + border-right: 1px solid #aaa; + overflow: auto; + } + .aside .toggle_aside { + position: absolute; + right: 0; + display: inline-block; + width: 26px; + height: 26px; + margin: 0 10px 0 0; + border: 1px solid #ccc; + border-radius: 20px; + text-align: center; + line-height: 26px; + } + .aside .categories { + margin: 30px 0; + } + + #nav_entries { + width: 100%; + } + + .nav_menu .btn { + margin: 5px 10px; + } + .nav_menu .stick { + margin: 0 10px; + } + .nav_menu .stick .btn { + margin: 5px 0; + } + .nav_menu .search { + display: inline-block; + max-width: 97%; + } + .nav_menu .search input { + max-width: 97%; + width: 90px; + } + .nav_menu .search input:focus { + width: 400px; + } + + #panel { + left: 5px; right: 5px; + } + + .day .date { + display: none; + } + .day .name { + height: 2.6em; + font-size: 1em; + text-shadow: none; + } + + .notification, + .actualizeProgress { + left: 10px; + right: 10px; + } +} + +/*** FALLBACK ***/ +.btn { + background: #1c1c1c; +} + .btn:hover { + background: -moz-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + background: -webkit-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + background: -o-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + background: -ms-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + } + .btn-important { + background: #26303F; + } + .btn-important:hover { + background: -moz-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + background: -webkit-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + background: -o-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + background: -ms-linear-gradient(top, #4A5D7A 0%, #26303F 100%); + } + .btn-attention { + background: #880011; + } + .btn-attention:hover { + background: -moz-linear-gradient(top, #cc0044 0%, #880011 100%); + background: -webkit-linear-gradient(top, #cc0044 0%, #880011 100%); + background: -o-linear-gradient(top, #cc0044 0%, #880011 100%); + background: -ms-linear-gradient(top, #cc0044 0%, #880011 100%); + } + +.dropdown-menu:after { + -moz-transform: rotate(45deg); + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); +} + +.nav-head { + background: #fff; + background: -moz-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -webkit-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -o-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%); +} + +.header > .item.search input { + -moz-transition: width 200ms linear; + -webkit-transition: width 200ms linear; + -o-transition: width 200ms linear; + -ms-transition: width 200ms linear; +} + +@media(max-width: 840px) { + .aside { + -moz-transition: width 200ms linear; + -webkit-transition: width 200ms linear; + -o-transition: width 200ms linear; + -ms-transition: width 200ms linear; + } +} + +@media print { + .header, + .aside, + .nav_menu, + .day, + .flux_header, + .flux_content .bottom, + .pagination { + display: none; + } + + html, body { + background: #fff; + color: #000; + font-family: Serif; + font-size: 12pt; + } + + #global, + .flux_content { + display: block !important; + } + + .flux_content .content { + width: 100% !important; + text-align: justify; + } + + .flux_content .content a { + color: #000; + } + .flux_content .content a:after { + content: " (" attr(href) ") "; + text-decoration: underline; + } +} diff --git a/p/themes/Dark/global.css b/p/themes/Dark/global.css new file mode 100644 index 000000000..04eb723f1 --- /dev/null +++ b/p/themes/Dark/global.css @@ -0,0 +1,487 @@ +@charset "UTF-8"; + +/* FONTS */ +@font-face { + font-family: "OpenSans"; + src: url("../fonts/openSans.woff") format("woff"); +} + + +* { + margin: 0; + padding: 0; +} +html, body { + height: 100%; + font-size: 95%; + font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif; + color: #888; +} + +/* LIENS */ +a { + color: #6986B2; + text-decoration: none; +} + a:hover { + text-decoration: underline; + } + +/* LISTES */ +ul, ol, dl { + margin: 10px 0 10px 30px; + line-height: 190%; +} + dd { + margin: 0 0 10px 30px; + } + +/* TITRES */ +h1, h2, h3 { + min-height: 40px; + margin: 15px 0 5px; + line-height: 40px; +} + +/* IMG */ +figure { + margin: 5px 0 10px; + text-align: center; +} + figcaption { + display: inline-block; + padding: 3px 20px; + color: #999; + font-style: italic; + border-bottom: 1px solid #ccc; + } +img { + height: auto; + max-width: 100%; + vertical-align: middle; +} + a img { + border: none; + } + +/* VIDEOS */ +iframe, embed, object, video { + max-width: 100%; +} + +/* FORMULAIRES */ +legend { + display: block; + width: 100%; + margin: 20px 0 5px; + padding: 5px 0; + border-bottom: 1px solid #2f2f2f; + font-size: 150%; + clear: both; +} +label { + display: block; + min-height: 25px; + padding: 5px 0; + font-size: 14px; + line-height: 25px; + cursor: pointer; +} +input, select, textarea { + display: inline-block; + max-width: 100%; + min-height: 25px; + padding: 5px; + background: #333; + border: 1px solid #000; + border-radius: 3px; + color: #999; + line-height: 25px; + vertical-align: middle; + box-shadow: 0 2px 2px #1d1d1d inset; +} + option { + padding:0 .5em 0 .5em; + } + input[type="radio"], + input[type="checkbox"] { + width: 15px !important; + min-height: 15px !important; + } + input:focus, select:focus, textarea:focus { + color: #6986b2; + border-color: #2f2f2f; + } + input:invalid, select:invalid { + border-color: red; + box-shadow: 0 0 2px 1px red; + } + +.form-group { + margin: 0; +} + .form-group:after { + content: ""; + display: block; + clear: both; + } + .form-group.form-actions { + min-width: 250px; + padding: 5px 0; + background: #1a1a1a; + border-top: 1px solid #2f2f2f; + } + .form-group.form-actions .btn { + margin: 0 10px; + } + .form-group .group-name { + display: block; + float: left; + width: 200px; + padding: 10px 0; + text-align: right; + } + .form-group .group-controls { + min-width: 250px; + min-height: 25px; + margin: 0 0 0 220px; + padding: 5px 0; + } + .form-group .group-controls .control { + display: block; + min-height: 30px; + padding: 5px 0; + line-height: 25px; + font-size: 14px; + } + +.stick { + display: inline-block; + white-space: nowrap; + font-size: 0px; + vertical-align: middle; +} + .stick input, + .stick .btn { + border-radius: 0; + font-size: 14px; + } + .stick .btn:first-child, + .stick input:first-child { + border-radius: 3px 0 0 3px; + } + .stick .btn-important:first-child { + border-right: 1px solid #000; + } + .stick .btn:last-child, + .stick input:last-child { + border-radius: 0 3px 3px 0; + } + .stick .btn + .btn, + .stick .btn + input, + .stick input + .btn, + .stick input + input { + border-left: none; + } + .stick .btn + .dropdown > .btn { + border-left: none; + border-radius: 0 3px 3px 0; + } + .stick .btn + .dropdown a { + font-size: 12px; + } + +.btn { + display: inline-block; + min-height: 37px; + min-width: 15px; + padding: 5px 10px; + background: linear-gradient(to bottom, #fff 0%, #eee 100%); + border-radius: 3px; + border: 1px solid #000; + color: #888; + line-height: 20px; + vertical-align: middle; + cursor: pointer; + overflow: hidden; +} + a.btn { + min-height: 25px; + line-height: 25px; + } + .btn:hover { + background: linear-gradient(to bottom, #4A5D7A, #26303F); + text-decoration: none; + } + .btn.active, + .btn:active, + .dropdown-target:target ~ .btn.dropdown-toggle { + background: #26303F; + } + + .btn-important { + background: linear-gradient(to bottom, #0084CC, #0045CC); + color: #888888; + border: 1px solid #000000; + } + .btn-important:hover { + background: linear-gradient(to bottom, #0066CC, #0045CC); + } + .btn-important:active { + background: #0044CB; + box-shadow: none; + } + + .btn-attention { + background: linear-gradient(to bottom, #E95B57, #BD362F); + color: #888888; + border: 1px solid #000000; + } + .btn-attention:hover { + background: linear-gradient(to bottom, #D14641, #BD362F); + } + .btn-attention:active { + background: #BD362F; + box-shadow: none; + } + +/* NAVIGATION */ +.nav-list .nav-header, +.nav-list .item { + display: block; + height: 35px; + line-height: 35px; +} + .nav-list .item:hover { + background: #1a1a1a; + } + .nav-list .item:hover a { + color: #26303F; + } + .nav-list .item.active { + background: #26303F; + color: #1a1a1a; + } + .nav-list .item.active a { + color: #888; + } + .nav-list .disable { + color: #aaa; + background: #fafafa; + text-align: center; + } + .nav-list .item > * { + display: block; + padding: 0 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .nav-list a:hover { + text-decoration: none; + } + .nav-list .item.error a { + color: #BD362F; + } + .nav-list .item.active.error a { + color: #fff; + background: #BD362F; + } + .nav-list .item.empty a { + color: #f39c12; + } + .nav-list .item.active.empty a { + color: #fff; + background: #f39c12; + } + + .nav-list .nav-header { + padding: 0 10px; + background: #1a1a1a; + border-bottom: 1px solid #2f2f2f; + font-weight: bold; + } + .nav-list .separator { + display: block; + height: 0; + margin: 5px 0; + border-bottom: 1px solid #ddd; + } + + .nav-list .nav-form { + padding: 3px; + text-align: center; + } + +.nav-head { + display: block; + margin: 0; + background: linear-gradient(to bottom, #fff, #f0f0f0); + border-bottom: 1px solid #ddd; + text-align: right; +} + .nav-head .item { + display: inline-block; + padding: 5px 10px; + } + +/* HORIZONTAL-LIST */ +.horizontal-list { + display: table; + table-layout: fixed; + margin: 0; + padding: 0; + width: 100%; +} + .horizontal-list .item { + display: table-cell; + vertical-align: middle; + } + +/* DROPDOWN */ +.dropdown { + position: relative; + display: inline-block; +} + .dropdown-target { + display: none; + } + + .dropdown-menu { + display: none; + min-width: 200px; + margin: 5px 0 0; + padding: 5px 0; + position: absolute; + right: 0px; + background: #1a1a1a; + border: 1px solid #888; + border-radius: 5px; + text-align: left; + } + .dropdown-menu:after { + content: ""; + position: absolute; + top: -6px; + right: 13px; + width: 10px; + height: 10px; + background: #1a1a1a; + border-top: 1px solid #888; + border-left: 1px solid #888; + z-index: -10; + transform: rotate(45deg); + } + .dropdown-header { + display: block; + padding: 0 5px; + color: #888; + font-weight: bold; + font-size: 14px; + line-height: 30px; + } + .dropdown-menu .item { + display: block; + height: 30px; + font-size: 90%; + line-height: 30px; + } + .dropdown-menu > .item > a { + display: block; + padding: 0 25px; + line-height: 30px; + } + .dropdown-menu > .item:hover { + background: #26303F; + color: #888; + } + .dropdown-menu > .item[aria-checked="true"] > a:before { + content: '✓ '; + font-weight: bold; + margin: 0 0 0 -1.2em; + padding: 0 0.2em 0 0; + } + .dropdown-menu > .item:hover > a { + color: #888; + text-decoration: none; + } + .dropdown-menu .input { + display: block; + height: 40px; + font-size: 90%; + line-height: 30px; + } + .dropdown-menu .input select, + .dropdown-menu .input input { + display: block; + height: 20px; + width: 95%; + margin: auto; + padding: 2px 5px; + border-radius: 3px; + } + .dropdown-menu .input select { + width: 70%; + height: auto; + } + .dropdown-menu .separator { + display: block; + height: 0; + margin: 5px 0; + border-bottom: 1px solid #888; + } + .dropdown-target:target ~ .dropdown-menu { + display: block; + z-index: 10; + } + .dropdown-close { + display: inline; + } + .dropdown-close a { + font-size: 0; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + display: block; + z-index: -10; + } + +/* ALERTS */ +.alert { + display: block; + width: 90%; + margin: 15px auto; + padding: 10px 15px; + background: #1a1a1a; + border: 1px solid #ccc; + border-right: 1px solid #aaa; + border-bottom: 1px solid #aaa; + border-radius: 5px; + color: #aaa; +} + .alert-head { + margin: 0; + font-weight: bold; + font-size: 110%; + } + .alert-warn { + border: 1px solid #c95; + color: #c95; + } + .alert-success { + border: 1px solid #484; + color: #484; + } + .alert-error { + border: 1px solid #844; + color: #844; + } + +/* ICÔNES */ +.icon { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + line-height: 16px; +} diff --git a/p/themes/Dark/loader.gif b/p/themes/Dark/loader.gif new file mode 100644 index 000000000..86022be71 Binary files /dev/null and b/p/themes/Dark/loader.gif differ diff --git a/p/themes/Dark/metadata.json b/p/themes/Dark/metadata.json new file mode 100644 index 000000000..27cae27df --- /dev/null +++ b/p/themes/Dark/metadata.json @@ -0,0 +1,7 @@ +{ + "name": "Dark", + "author": "AD", + "description": "Le coté obscur du thème “Origine”", + "version": 0.1, + "files": ["global.css", "freshrss.css"] +} diff --git a/p/themes/Flat/freshrss.css b/p/themes/Flat/freshrss.css new file mode 100644 index 000000000..dca1b3f28 --- /dev/null +++ b/p/themes/Flat/freshrss.css @@ -0,0 +1,817 @@ +@charset "UTF-8"; + +/* STRUCTURE */ +body { + background: #fafafa; +} + +.header { + display: table; + width: 100%; + table-layout: fixed; + background: #ecf0f1; +} + .header > .item { + display: table-cell; + padding: 10px 0; + vertical-align: middle; + text-align: center; + } + .header > .item.title { + width: 250px; + white-space: nowrap; + } + .logo { + display: inline-block; + font-size: 48px; + height: 32px; + width: 32px; + padding: 10px; + } + .header > .item.title h1 { + display: inline-block; + margin: 0; + } + .header > .item.search input { + width: 230px; + transition: width 200ms linear; + } + .header .item.search input:focus { + width: 330px; + } + .header > .item.configure { + width: 100px; + } + +.item a:hover { + text-decoration: none; +} + +#global { + display: table; + width: 100%; + height: 100%; + table-layout: fixed; +} + .aside { + display: table-cell; + height: 100%; + width: 250px; + vertical-align: top; + background: #ecf0f1; + } + .aside .nav-form input { + width: 180px; + } + .aside.aside_flux { + padding: 10px 0 40px; + } + .aside.aside_feed .nav-form input { + width: 140px; + } + .aside.aside_feed .nav-form .dropdown-menu { + right: -20px; + } + .aside.aside_feed .nav-form .dropdown-menu:after { + right: 33px; + } + + .nav-login { + display: none; + } + + .nav_menu { + width: 100%; + text-align: center; + padding: 5px 0; + } + .nav_menu .search { + display:none; + } + +.favicon { + height: 16px; + width: 16px; +} + +.categories { + margin: 0; + padding: 0; + text-align: center; + list-style: none; +} + .category { + display: block; + width: 220px; + margin: 10px auto; + text-align: left; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .category .btn:first-child { + width: 195px; + position: relative; + } + .category.stick .btn:first-child { + width:160px; + } + .category .btn:first-child:not([data-unread="0"]):after { + content: attr(data-unread); + position: absolute; + top: 5px; right: 5px; + padding: 0 5px; + color: #fff; + font-size: 90%; + background: #3498DB; + border-radius: 5px; + } + .category + .feeds:not(.active) { + display:none; + } + .categories .feeds { + width: 100%; + margin: 0 auto; + list-style: none; + } + .categories .feeds .item.active:after { + content: "⇢"; + line-height: 35px; + float: right; + } + .categories .feeds .item.empty .feed { + color: #e67e22; + } + .categories .feeds .item.error .feed { + color: #BD362F; + } + .categories .feeds .item .feed { + display: inline-block; + margin: 0; + width: 165px; + line-height: 35px; + font-size: 90%; + vertical-align: middle; + text-align: left; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .feed:not([data-unread="0"]) { + font-weight:bold; + } + .feed:not([data-unread="0"]):before { + content: "(" attr(data-unread) ") "; + } + .categories .feeds .dropdown-menu { + left: 0; + } + .categories .feeds .dropdown-menu:after { + left: 2px; + } + .categories .feeds .item .dropdown-toggle > .icon { + visibility: hidden; + cursor: pointer; + } + .categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, + .categories .feeds .item:hover .dropdown-toggle > .icon, + .categories .feeds .item.active .dropdown-toggle > .icon { + background-color: #95a5a6; + border-radius: 3px; + visibility: visible; + } + .categories .btn:hover .notRead, + .categories .btn.active .notRead { + background: #2980B9; + border-left: 3px solid #3498DB; + } + +.post { + padding: 10px 50px; +} + .post form { + margin: 10px 0; + } + +.day { + padding: 5px 15px; + font-size: 130%; + font-weight: bold; + line-height: 50px; + border-left: 3px solid #ecf0f1; +} + .day .name { + position: absolute; + right: 0; + width: 50%; + height: 1.5em; + padding: 0 10px 0 0; + overflow: hidden; + color: #aab; + font-size: 1.8em; + opacity: .3; + font-style: italic; + text-align: right; + white-space: nowrap; + text-overflow: ellipsis; + z-index: -10; + } + +.flux { + border-left: 3px solid #ecf0f1; +} + .flux:hover { + background: #fff; + } + .flux.not_read { + border-left-color: #FF5300; + background: #FFF3ED; + } + .flux.favorite { + border-left-color: #FFC300; + background: #FFF6DA; + } + .flux.current { + border-left-color: #3498db; + background: #fff; + } + + .flux_header { + background: inherit; + height: 25px; + font-size: 12px; + border-top: 1px solid #ecf0f1; + cursor: pointer; + } + .flux .item { + line-height: 40px; + white-space: nowrap; + } + .flux_header > .item { + overflow: hidden; + text-overflow: ellipsis; + } + .flux .item.manage { + width: 40px; + text-align: center; + } + .flux .item.website { + width: 200px; + } + .website .favicon { + padding: 5px; + } + .flux .item.title { + background: inherit; + } + .flux:hover .item.title { + border-right: 2px solid rgba(127, 127, 127, 0.1); + padding-right: 1em; + position: absolute; + } + .flux .item.title a { + color: #333; + outline: none; + } + .flux.current .item.title, + .flux.not_read .item.title { + font-weight: bold; + } + .flux .item.date { + width: 200px; + padding:0 5px 0 0; + text-align: right; + font-size: 10px; + color: #666; + } + .link { + width: 40px; + text-align: center; + } + +#stream.reader .flux { + position: relative; + padding: 0 0 30px; + border: none; + background: #ecf0f1; + color: #34495e; + font-size: 120%; +} + #stream.reader .flux .author { + margin: 0 0 10px; + font-size: 90%; + color: #aaa; + } + +#stream.global { + text-align: center; +} + #stream.global .box-category { + display: inline-block; + width: 280px; + margin: 20px 10px; + vertical-align: top; + border: 1px solid #ddd; + border-radius: 5px; + text-align: left; + } + #stream.global .category { + width: 100%; + margin: 0; + } + #stream.global .btn { + display: block; + width: auto; + height: 35px; + margin: 0; + padding: 0 10px; + background: #ecf0f1; + color: #333; + border-bottom: 1px solid #ddd; + border-radius: 5px 5px 0 0; + line-height: 35px; + font-size: 120%; + } + #stream.global .btn:not([data-unread="0"]) { + font-weight:bold; + } + #stream.global .btn:first-child:not([data-unread="0"]):after { + top: 0; right: 5px; + border: 0; + background: none; + color: #333; + font-weight: bold; + box-shadow: none; + } + #stream.global .box-category .feeds { + display: block; + max-height: 250px; + margin: 0; + list-style: none; + overflow: auto; + } + #stream.global .box-category .feeds .item { + padding: 2px 10px; + font-size: 90%; + } + #stream.global .box-category .feed { + width: 220px; + } + +.content { + min-height: 150px; + max-width: 550px; + margin: 0 auto; + padding: 20px 10px; + line-height: 170%; + word-wrap: break-word; +} + .content h1, .content h2, .content h3 { + margin: 20px 0 5px; + } + .content > .title { + font-size: x-large; + margin: 0; + } + + .content p { + margin: 0 0 20px; + } + img.big { + display: block; + margin: 10px auto; + } + figure img.big { + margin: 0; + } + .content hr { + margin: 30px 0; + height: 1px; + background: #ddd; + border: 0; + } + .content pre { + margin: 10px auto; + padding: 10px; + overflow: auto; + background: #000; + color: #fff; + font-size: 110%; + } + .content q, .content blockquote { + display: block; + margin: 5px 0; + padding: 5px 20px; + font-style: italic; + border-left: 4px solid #ccc; + color: #666; + } + .content blockquote p { + margin: 0; + } + +#panel { + display: none; + position: fixed; + top: 10px; bottom: 10px; + left: 100px; right: 100px; + overflow: auto; + background: #fff; + border: 1px solid #95a5a6; + border-radius: 5px; +} + #panel .close { + position: fixed; + top: 10px; right: 0px; + display: inline-block; + width: 26px; + height: 26px; + margin: 0 10px 0 0; + border-radius: 3px; + text-align: center; + line-height: 24px; + background: #95a5a6; + } + #panel .close:hover { + background: #7f8c8d; + } + +#overlay { + display: none; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + background: rgba(0, 0, 0, 0.9); +} + +.flux_content .bottom { + font-size: 90%; + text-align: center; +} + +.hide_posts > :not(.active) > .flux_content { + display:none; +} + +/*** PAGINATION ***/ +.pagination { + display: table; + width: 100%; + margin: 0; + background: #ecf0f1; + text-align: center; + color: #000; + font-size: 80%; + line-height: 200%; + table-layout: fixed; + font-weight: bold; +} + .pagination .item { + display: table-cell; + line-height: 40px; + vertical-align: top; + } + .pagination .item.pager-current { + font-weight: bold; + font-size: 140%; + color: #ecf0f1; + background: #34495e; + } + .pagination .item.pager-first, + .pagination .item.pager-previous, + .pagination .item.pager-next, + .pagination .item.pager-last { + width: 100px; + } + .pagination .item a { + display: block; + color: #000; + font-weight: bold; + line-height: 40px; + } + .pagination .item a:hover { + color: #ecf0f1; + background: #34495e; + } + +#nav_entries { + display: table; + width: 250px; + height: 40px; + position: fixed; + bottom: 0; + left: 0; + margin: 0; + background: #34495e; + text-align: center; + line-height: 40px; + table-layout: fixed; +} + #nav_entries .item { + display: table-cell; + width: 30%; + } + #nav_entries a { + display: block; + } + #nav_entries .i_up { + margin: 5px 0 0; + vertical-align: top; + } + +.pagination .loading, +.pagination a:hover.loading { + background: url("loader.gif") center center no-repeat #34495e; + font-size: 0; +} + +#bigMarkAsRead { + background: #ecf0f1; + display: block; + font-style: normal; + padding: 32px 0 64px 0; + text-align: center; + text-decoration: none; + text-shadow: 0 -1px 0 #aaa; +} + #bigMarkAsRead:hover { + background: #34495e; + color: #fff; + } + .bigTick { + font-size: 72pt; + line-height: 1.6em; + } + +/*** NOTIFICATION ***/ +.notification { + position: absolute; + top: 10px; + left: 25%; right: 25%; + min-height: 30px; + padding: 10px; + line-height: 30px; + text-align: center; + border-radius: 3px; + background: #ddd; + color: #666; + font-weight: bold; + z-index: 10; +} + .notification.good { + background: #1abc9c; + color: #fff; + } + .notification.bad { + background: #e74c3c; + color: #fff; + } + .notification a.close { + display: inline-block; + width: 16px; + height: 16px; + float: right; + margin: -16px -16px 0 0; + padding: 5px; + border-radius: 3px; + line-height: 16px; + } + .notification.good a.close { + background: #1abc9c; + } + .notification.bad a.close { + background: #e74c3c; + } + +.toggle_aside, .btn.toggle_aside { + display: none; +} + +.actualizeProgress { + position: fixed; + top: 10px; + left: 25%; right: 25%; + padding: 5px; + background: #3498db; + color: #fff; + text-align: center; + border-radius: 3px; + font-weight: bold; +} + .actualizeProgress progress { + max-width: 100%; + width: 250px; + height: 15px; + vertical-align: middle; + background: #fff; + border: none; + } + .actualizeProgress .progress { + color: #ecf0f1; + } + +.logs { + border: 1px solid #34495e; +} + .log { + margin: 10px 0; + padding: 5px 2%; + overflow: auto; + background: #fafafa; + color: #666; + font-size: 90%; + } + .log>.date { + margin: 0 10px 0 0; + padding: 5px 10px; + border-radius: 20px; + } + .log.error>.date { + background: #e74c3c; + color: #fff; + } + .log.warning>.date { + background: #f39c12; + } + .log.notice>.date { + background: #ecf0f1; + } + .log.debug>.date { + background: #111; + color: #eee; + } + +.form-group table { + border-collapse:collapse; + margin:10px 0 0 220px; + text-align:center; +} + +.form-group tr, .form-group th, .form-group td { + font-weight:normal; + padding:.5em; +} + +select.number option { + text-align:right; +} + +@media(max-width: 840px) { + .header, + .aside .btn-important, + .aside .feeds .dropdown, + .flux_header .item.website span, + .item.date { + display: none; + } + .flux_header .item.website { + width: 40px; + text-align: center; + } + .flux_header .item.website .favicon { + padding: 12px; + } + + .nav-login { + display: block; + } + + .content { + font-size: 120%; + padding: 0; + } + + .pagination { + margin: 0 0 40px; + } + .pagination .pager-previous, .pagination .pager-next { + width: 100px; + } + + .toggle_aside, .btn.toggle_aside { + display: inline-block; + } + .aside { + position: fixed; + top: 0; left: 0; + width: 0; + overflow: hidden; + z-index: 10; + transition: width 200ms linear; + background: #ecf0f1; + } + .aside.aside_flux { + padding: 10px 0 0; + } + .aside:target { + width: 80%; + border-right: 1px solid #aaa; + overflow: auto; + } + .aside .toggle_aside { + position: absolute; + right: 10px; + display: inline-block; + width: 26px; + height: 26px; + margin: 0 10px 0 0; + border-radius: 3px; + text-align: center; + line-height: 24px; + background: #95a5a6; + } + .aside .toggle_aside:hover { + background: #7f8c8d; + } + .aside .categories { + margin: 30px 0; + } + + #nav_entries { + width: 100%; + } + + .nav_menu .btn { + margin: 5px 10px; + } + .nav_menu .stick { + margin: 0 10px; + } + .nav_menu .stick .btn { + margin: 5px 0; + } + .nav_menu .search { + display: inline-block; + max-width: 97%; + } + .nav_menu .search input { + max-width: 97%; + width: 90px; + } + .nav_menu .search input:focus { + width: 400px; + } + + #panel { + left: 5px; right: 5px; + } + + .day .date { + display: none; + } + .day .name { + height: 2.6em; + font-size: 1em; + text-shadow: none; + } + + .notification, + .actualizeProgress { + left: 10px; + right: 10px; + } +} + +/*** FALLBACK ***/ +.dropdown-menu:after { + -moz-transform: rotate(45deg); + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); +} + +@media print { + .header, + .aside, + .nav_menu, + .day, + .flux_header, + .flux_content .bottom, + .pagination { + display: none; + } + + html, body { + background: #fff; + color: #000; + font-family: Serif; + font-size: 12pt; + } + + #global, + .flux_content { + display: block !important; + } + + .flux_content .content { + width: 100% !important; + text-align: justify; + } + + .flux_content .content a { + color: #000; + } + .flux_content .content a:after { + content: " (" attr(href) ") "; + text-decoration: underline; + } +} diff --git a/p/themes/Flat/global.css b/p/themes/Flat/global.css new file mode 100644 index 000000000..5fd0a4dcf --- /dev/null +++ b/p/themes/Flat/global.css @@ -0,0 +1,495 @@ +@charset "UTF-8"; + +/* FONTS */ +@font-face { + font-family: "OpenSans"; + src: url("../fonts/openSans.woff") format("woff"); +} + + +* { + margin: 0; + padding: 0; +} +html, body { + height: 100%; + font-size: 95%; + font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif; +} + +/* LIENS */ +a { + color: #2980b9; + text-decoration: none; +} + a:hover { + text-decoration: underline; + } + +/* LISTES */ +ul, ol, dl { + margin: 10px 0 10px 30px; + line-height: 190%; +} + dd { + margin: 0 0 10px 30px; + } + +/* TITRES */ +h1, h2, h3 { + min-height: 40px; + margin: 15px 0 5px; + line-height: 40px; +} + +/* IMG */ +figure { + margin: 5px 0 10px; + text-align: center; +} + figcaption { + display: inline-block; + padding: 3px 20px; + color: #999; + font-style: italic; + border-bottom: 1px solid #ccc; + } +img { + height: auto; + max-width: 100%; + vertical-align: middle; +} + a img { + border: none; + } + +/* VIDEOS */ +iframe, embed, object, video { + max-width: 100%; +} + +/* FORMULAIRES */ +legend { + display: inline-block; + margin: 20px 0 5px; + padding: 5px 20px; + font-size: 150%; + clear: both; + background: #ecf0f1; + border-radius: 20px; +} +label { + display: block; + min-height: 25px; + padding: 5px 0; + font-size: 12px; + line-height: 25px; + cursor: pointer; + font-weight: bold; + color: #444; +} +input, select, textarea { + display: inline-block; + max-width: 100%; + min-height: 25px; + padding: 5px; + background: #FFF; + border: none; + border-bottom: 3px solid #ddd; + color: #666; + line-height: 25px; + vertical-align: middle; + border-radius: 5px; +} + option { + padding:0 .5em 0 .5em; + } + input[type="radio"], + input[type="checkbox"] { + width: 15px !important; + min-height: 15px !important; + } + input:focus, select:focus, textarea:focus { + color: #333; + border-color: #2980b9; + } + input:invalid, select:invalid { + border-color: red; + box-shadow: 0 0 2px 1px red; + } + +.form-group { + margin: 5px 0; + border: 1px solid transparent; +} + .form-group:after { + content: ""; + display: block; + clear: both; + } + .form-group:hover { + background: #fff; + border: 1px solid #eee; + border-radius: 3px; + } + .form-group.form-actions { + min-width: 250px; + margin: 20px 0; + padding: 5px 0; + background: #ecf0f1; + border-top: 3px solid #bdc3c7; + border-radius: 5px 5px 0 0; + } + .form-group.form-actions .btn { + margin: 0 10px; + } + .form-group .group-name { + display: block; + float: left; + width: 200px; + padding: 10px 0; + text-align: right; + } + .form-group .group-controls { + min-width: 250px; + min-height: 25px; + margin: 0 0 0 220px; + padding: 5px 0; + } + .form-group .group-controls label { + font-weight: normal; + font-size: 14px; + color: #000; + } + .form-group .group-controls .control { + display: block; + min-height: 30px; + padding: 5px 0; + line-height: 25px; + font-size: 14px; + } + +.stick { + display: inline-block; + white-space: nowrap; + font-size: 0px; + vertical-align: middle; +} + .stick .btn, + .stick input { + font-size: 14px; + border-radius: 0; + } + .stick .btn:first-child, + .stick input:first-child { + border-radius: 5px 0 0 5px; + } + .stick .btn:last-child, + .stick input:last-child, + .stick .btn + .dropdown > .btn { + border-radius: 0 5px 5px 0; + } + .stick .btn + .dropdown a { + font-size: 12px; + } + +.btn { + display: inline-block; + min-height: 38px; + min-width: 18px; + padding: 5px 10px; + background: #3498db; + border-radius: 5px; + border: none; + border-bottom: 3px solid #2980b9; + color: #fff; + line-height: 20px; + vertical-align: middle; + cursor: pointer; + overflow: hidden; +} + a.btn { + min-height: 25px; + line-height: 25px; + } + .btn.active, + .btn:active, + .btn:hover, + .dropdown-target:target ~ .btn.dropdown-toggle { + background: #2980b9; + text-decoration: none; + } + + .btn-important { + background: #e67e22; + color: #fff; + border-bottom: 3px solid #d35400; + } + .btn-important:active, + .btn-important:hover { + background: #d35400; + } + + .btn-attention { + background: #e74c3c; + color: #fff; + border-bottom: 3px solid #c0392b; + } + .btn-attention:hover, + .btn-attention:active { + background: #c0392b; + } + +/* NAVIGATION */ +.nav-list { + border-right: 1px solid #ecf0f1; +} +.nav-list .nav-header, +.nav-list .item { + display: block; + height: 35px; + line-height: 35px; + margin: 5px 0; +} + .nav-list .item:hover, + .nav-list .item.active { + background: #2980b9; + color: #fff; + } + .nav-list .item:hover a, + .nav-list .item.active a { + color: #fff; + } + .nav-list .disable { + color: #aaa; + background: #fafafa; + text-align: center; + } + .nav-list .item > * { + display: block; + padding: 0 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .nav-list a:hover { + text-decoration: none; + } + .nav-list .item.error a { + color: #BD362F; + } + .nav-list .item:hover.error a, + .nav-list .item.active.error a { + color: #fff; + background: #BD362F; + } + .nav-list .item.empty a { + color: #f39c12; + } + .nav-list .item:hover.empty a, + .nav-list .item.active.empty a { + color: #fff; + background: #f39c12; + } + + .nav-list .nav-header { + padding: 0 10px; + margin: 0; + color: #fff; + background: #34495e; + font-weight: bold; + } + .nav-list .separator { + display: block; + height: 0; + margin: 5px 0; + border-bottom: 1px solid #ddd; + } + + .nav-list .nav-form { + padding: 3px; + text-align: center; + } + +.nav-head { + display: block; + margin: 0; + background: #34495e; + color: #fff; + text-align: right; +} + .nav-head a { + color: #fff; + } + .nav-head .item { + display: inline-block; + padding: 5px 10px; + } + +/* HORIZONTAL-LIST */ +.horizontal-list { + display: table; + table-layout: fixed; + margin: 0; + padding: 0; + width: 100%; +} + .horizontal-list .item { + display: table-cell; + vertical-align: middle; + } + +/* DROPDOWN */ +.dropdown { + position: relative; + display: inline-block; +} + .dropdown-target { + display: none; + } + + .dropdown-menu { + display: none; + min-width: 200px; + margin: 5px 0 0; + padding: 5px 0; + position: absolute; + right: 0px; + background: #fff; + border: 1px solid #95a5a6; + border-radius: 3px; + text-align: left; + } + .dropdown-menu:after { + content: ""; + position: absolute; + top: -6px; + right: 13px; + width: 10px; + height: 10px; + background: #fff; + border-top: 1px solid #95a5a6; + border-left: 1px solid #95a5a6; + z-index: -10; + transform: rotate(45deg); + } + .dropdown-header { + display: block; + padding: 0 5px; + color: #34495e; + font-weight: bold; + font-size: 14px; + line-height: 30px; + } + .dropdown-menu > .item { + display: block; + height: 30px; + font-size: 90%; + line-height: 30px; + } + .dropdown-menu > .item > a { + display: block; + padding: 0 25px; + line-height: 30px; + } + .dropdown-menu > .item:hover > a { + background: #2980b9; + color: #fff; + } + .dropdown-menu > .item[aria-checked="true"] > a:before { + content: '✓ '; + font-weight: bold; + margin: 0 0 0 -1.2em; + padding: 0 0.2em 0 0; + } + .dropdown-menu > .item:hover > a { + color: #fff; + text-decoration: none; + } + .dropdown-menu .input { + display: block; + height: 40px; + font-size: 90%; + line-height: 30px; + } + .dropdown-menu label { + font-weight: normal; + } + .dropdown-menu .input select, + .dropdown-menu .input input { + display: block; + height: 20px; + width: 95%; + margin: auto; + padding: 2px 5px; + border-radius: 3px; + } + .dropdown-menu .input select { + width: 70%; + height: auto; + } + .dropdown-menu .separator { + display: block; + height: 0; + margin: 5px 0; + border-bottom: 1px solid #95a5a6; + } + .dropdown-target:target ~ .dropdown-menu { + display: block; + z-index: 10; + } + .dropdown-close { + display: inline; + } + .dropdown-close a { + font-size: 0; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + display: block; + z-index: -10; + } + +/* ALERTS */ +.alert { + display: block; + width: 90%; + margin: 15px auto; + padding: 10px 15px; + background: #f4f4f4; + border: 1px solid #ccc; + border-right: 1px solid #aaa; + border-bottom: 1px solid #aaa; + border-radius: 5px; + color: #aaa; + text-shadow: 0 0 1px #eee; +} + .alert-head { + margin: 0; + font-weight: bold; + font-size: 110%; + } + .alert-warn { + background: #ffe; + border: 1px solid #eeb; + color: #c95; + } + .alert-success { + background: #dfd; + border: 1px solid #cec; + color: #484; + } + .alert-error { + background: #fdd; + border: 1px solid #ecc; + color: #844; + } + +/* ICÔNES */ +.icon { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + line-height: 16px; +} diff --git a/p/themes/Flat/icons/add.svg b/p/themes/Flat/icons/add.svg new file mode 100644 index 000000000..15767a3ad --- /dev/null +++ b/p/themes/Flat/icons/add.svg @@ -0,0 +1,30 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/all.svg b/p/themes/Flat/icons/all.svg new file mode 100644 index 000000000..d20e0f5bf --- /dev/null +++ b/p/themes/Flat/icons/all.svg @@ -0,0 +1,32 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/close.svg b/p/themes/Flat/icons/close.svg new file mode 100644 index 000000000..629fda7ff --- /dev/null +++ b/p/themes/Flat/icons/close.svg @@ -0,0 +1,28 @@ + + + + + Gnome Symbolic Icon Theme + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/configure.svg b/p/themes/Flat/icons/configure.svg new file mode 100644 index 000000000..969c5719f --- /dev/null +++ b/p/themes/Flat/icons/configure.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/down.svg b/p/themes/Flat/icons/down.svg new file mode 100644 index 000000000..31730626f --- /dev/null +++ b/p/themes/Flat/icons/down.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/next.svg b/p/themes/Flat/icons/next.svg new file mode 100644 index 000000000..d75cc40f5 --- /dev/null +++ b/p/themes/Flat/icons/next.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/prev.svg b/p/themes/Flat/icons/prev.svg new file mode 100644 index 000000000..9ba03ceb2 --- /dev/null +++ b/p/themes/Flat/icons/prev.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/refresh.svg b/p/themes/Flat/icons/refresh.svg new file mode 100644 index 000000000..8f95bf443 --- /dev/null +++ b/p/themes/Flat/icons/refresh.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/search.svg b/p/themes/Flat/icons/search.svg new file mode 100644 index 000000000..bca7571b4 --- /dev/null +++ b/p/themes/Flat/icons/search.svg @@ -0,0 +1,32 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + + diff --git a/p/themes/Flat/icons/up.svg b/p/themes/Flat/icons/up.svg new file mode 100644 index 000000000..3ab11b168 --- /dev/null +++ b/p/themes/Flat/icons/up.svg @@ -0,0 +1,31 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + + + diff --git a/p/themes/Flat/loader.gif b/p/themes/Flat/loader.gif new file mode 100644 index 000000000..ce36565b3 Binary files /dev/null and b/p/themes/Flat/loader.gif differ diff --git a/p/themes/Flat/metadata.json b/p/themes/Flat/metadata.json new file mode 100644 index 000000000..6b94d11c2 --- /dev/null +++ b/p/themes/Flat/metadata.json @@ -0,0 +1,7 @@ +{ + "name": "Flat design", + "author": "Marien Fressinaud", + "description": "Thème plat pour FreshRSS", + "version": 0.1, + "files": ["global.css", "freshrss.css"] +} \ No newline at end of file diff --git a/p/themes/Origine/freshrss.css b/p/themes/Origine/freshrss.css new file mode 100644 index 000000000..593f21d30 --- /dev/null +++ b/p/themes/Origine/freshrss.css @@ -0,0 +1,880 @@ +@charset "UTF-8"; + +/* STRUCTURE */ +.header { + display: table; + width: 100%; + background: #f4f4f4; + table-layout: fixed; +} + .header > .item { + display: table-cell; + padding: 10px 0; + border-bottom: 1px solid #aaa; + vertical-align: middle; + text-align: center; + } + .header > .item.title { + width: 250px; + white-space: nowrap; + } + .logo { + display: inline-block; + font-size: 48px; + height: 32px; + width: 32px; + padding: 10px; + } + .header > .item.title h1 { + display: inline-block; + margin: 0; + text-shadow: 1px -1px 0 #ccc; + } + .header > .item.search input { + width: 230px; + transition: width 200ms linear; + } + .header .item.search input:focus { + width: 330px; + } + .header > .item.configure { + width: 100px; + } + +.item a:hover { + text-decoration: none; +} + +#global { + display: table; + width: 100%; + height: 100%; + background: #fafafa; + table-layout: fixed; +} + .aside { + display: table-cell; + height: 100%; + width: 250px; + vertical-align: top; + border-right: 1px solid #aaa; + background: #fff; + } + .aside .nav-form input { + width: 180px; + } + .aside.aside_flux { + padding: 10px 0 40px; + } + .aside.aside_feed .nav-form input { + width: 140px; + } + .aside.aside_feed .nav-form .dropdown .dropdown-menu { + right: -20px; + } + .aside.aside_feed .nav-form .dropdown .dropdown-menu:after { + right: 33px; + } + + .nav-login { + display: none; + } + + .nav_menu { + width: 100%; + background: #fafafa; + border-bottom: 1px solid #aaa; + text-align: center; + padding: 5px 0; + } + .nav_menu .search { + display:none; + } + +.favicon { + height: 16px; + width: 16px; +} + +.categories { + margin: 0; + padding: 0; + text-align: center; + list-style: none; +} + .category { + display: block; + width: 220px; + margin: 10px auto; + text-align: left; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .category .btn:first-child { + width: 195px; + position: relative; + } + .category.stick .btn:first-child { + width:160px; + } + .category .btn:first-child:not([data-unread="0"]):after { + content: attr(data-unread); + position: absolute; + top: 3px; right: 3px; + padding: 1px 5px; + background: #ccc; + color: #fff; + font-size: 90%; + border: 1px solid #bbb; + border-radius: 5px; + box-shadow: 1px 3px 3px #aaa inset; + text-shadow: 0 0 1px #aaa; + } + .category + .feeds:not(.active) { + display:none; + } + .categories .feeds { + width: 100%; + margin: 0; + list-style: none; + } + .categories .feeds .item.active { + background: #0062BE; + } + .categories .feeds .item.active .feed { + color: #fff; + } + .categories .feeds .item.empty .feed { + color: #e67e22; + } + .categories .feeds .item.empty.active { + background: #e67e22; + } + .categories .feeds .item.empty.active .feed { + color: #fff; + } + .categories .feeds .item.error .feed { + color: #BD362F; + } + .categories .feeds .item .feed { + display: inline-block; + margin: 0; + width: 165px; + line-height: 35px; + font-size: 90%; + vertical-align: middle; + text-align: left; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .feed:not([data-unread="0"]) { + font-weight:bold; + } + .feed:not([data-unread="0"]):before { + content: "(" attr(data-unread) ") "; + } + .categories .feeds .dropdown-menu { + left: 0; + } + .categories .feeds .dropdown-menu:after { + left: 2px; + } + .categories .feeds .item .dropdown-toggle > .icon { + visibility: hidden; + cursor: pointer; + } + .categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, + .categories .feeds .item:hover .dropdown-toggle > .icon, + .categories .feeds .item.active .dropdown-toggle > .icon { + background-color: #fff; + border-radius: 3px; + visibility: visible; + } + +.post { + padding: 10px 50px; +} + .post form { + margin: 10px 0; + } + +.day { + min-height: 50px; + padding: 0 10px; + font-size: 130%; + font-weight: bold; + line-height: 50px; + background: #fff; + border-top: 1px solid #aaa; + border-bottom: 1px solid #aaa; +} + .day:first-child { + border-top: none; + } + .day .name { + position: absolute; + right: 0; + width: 50%; + height: 1.5em; + padding: 0 10px 0 0; + overflow: hidden; + color: #aab; + font-size: 1.8em; + opacity: .3; + text-shadow: 0px -1px 0px #333; + font-style: italic; + white-space: nowrap; + text-overflow: ellipsis; + text-align: right; + } + +.flux { + border-left: 3px solid #aaa; + background: #fafafa; +} + .flux:hover { + background: #fff; + } + .flux.not_read { + border-left: 3px solid #FF5300; + background: #FFF3ED; + } + .flux.favorite { + border-left: 3px solid #FFC300; + background: #FFF6DA; + } + .flux.current { + border-left: 3px solid #0062BE; + background: #fff; + } + + .flux_header { + background: inherit; + height: 25px; + font-size: 12px; + border-top: 1px solid #ddd; + cursor: pointer; + } + .flux .item { + line-height: 40px; + white-space: nowrap; + } + .flux_header > .item { + overflow: hidden; + text-overflow: ellipsis; + } + .flux .item.manage { + width: 40px; + text-align: center; + } + .flux .item.website { + width: 200px; + } + .website .favicon { + padding: 5px; + } + .flux .item.title { + background: inherit; + } + .flux:hover .item.title { + border-right: 2px solid rgba(127, 127, 127, 0.1); + padding-right: 1em; + position: absolute; + } + .flux .item.title a { + color: #000; + outline: none; + } + .flux.not_read .item.title, + .flux.current .item.title { + font-weight: bold; + } + .flux .item.date { + width: 200px; + padding:0 5px 0 0; + text-align: right; + font-size: 10px; + color: #666; + } + .link { + width: 40px; + text-align: center; + } + +#stream.reader .flux { + padding: 0 0 30px; + border: none; + background: #f0f0f0; + color: #333; +} + #stream.reader .flux .author { + margin: 0 0 10px; + font-size: 90%; + color: #666; + } + +#stream.global { + text-align: center; +} + #stream.global .box-category { + display: inline-block; + width: 280px; + margin: 20px 10px; + vertical-align: top; + background: #fff; + border: 1px solid #aaa; + border-radius: 5px; + text-align: left; + box-shadow: 0 0 5px #bbb; + } + #stream.global .category { + width: 100%; + margin: 0; + } + #stream.global .btn { + display: block; + width: auto; + height: 35px; + margin: 0; + padding: 0 10px; + background: #eee; + border: none; + border-bottom: 1px solid #aaa; + border-radius: 5px 5px 0 0; + line-height: 35px; + font-size: 120%; + } + #stream.global .btn:not([data-unread="0"]) { + font-weight:bold; + } + #stream.global .btn:first-child:not([data-unread="0"]):after { + top: 0; right: 5px; + border: 0; + background: none; + color: #666; + font-weight: bold; + box-shadow: none; + } + #stream.global .box-category .feeds { + display: block; + max-height: 250px; + margin: 0; + list-style: none; + overflow: auto; + } + #stream.global .box-category .feeds .item { + padding: 2px 10px; + font-size: 90%; + } + #stream.global .box-category .feed { + width: 220px; + } + +.content { + min-height: 150px; + max-width: 550px; + margin: 0 auto; + padding: 20px 10px; + line-height: 170%; + word-wrap: break-word; +} + .content h1, .content h2, .content h3 { + margin: 20px 0 5px; + } + .content > .title { + font-size: x-large; + margin: 0; + } + + .content p { + margin: 0 0 20px; + } + img.big { + display: block; + margin: 10px auto; + } + figure img.big { + margin: 0; + } + .content hr { + margin: 30px 0; + height: 1px; + background: #ddd; + border: 0; + } + .content pre { + margin: 10px auto; + padding: 10px; + overflow: auto; + background: #000; + color: #fff; + font-size: 110%; + } + .content q, .content blockquote { + display: block; + margin: 5px 0; + padding: 5px 20px; + font-style: italic; + border-left: 4px solid #ccc; + color: #666; + } + .content blockquote p { + margin: 0; + } + +#panel { + display: none; + position: fixed; + top: 10px; bottom: 10px; + left: 100px; right: 100px; + overflow: auto; + background: #fff; + border: 1px solid #95a5a6; + border-radius: 5px; +} + #panel .close { + position: fixed; + top: 10px; right: 0; + display: inline-block; + width: 26px; + height: 26px; + margin: 0 10px 0 0; + border: 1px solid #ccc; + border-radius: 20px; + text-align: center; + line-height: 26px; + background: #fff; + } + +#overlay { + display: none; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + background: rgba(0, 0, 0, 0.9); +} + +.flux_content .bottom { + font-size: 90%; + text-align: center; +} + +.hide_posts > :not(.active) > .flux_content { + display:none; +} + +/*** PAGINATION ***/ +.pagination { + display: table; + width: 100%; + margin: 0; + background: #fafafa; + text-align: center; + color: #333; + font-size: 80%; + line-height: 200%; + table-layout: fixed; +} + .pagination .item { + display: table-cell; + line-height: 40px; + } + .pagination .item.pager-current { + font-weight: bold; + font-size: 140%; + } + .pagination .pager-first, + .pagination .pager-previous, + .pagination .pager-next, + .pagination .pager-last { + width: 100px; + } + .pagination .item a { + display: block; + color: #333; + font-style: italic; + } + .pagination:first-child .item { + border-bottom: 1px solid #aaa; + } + .pagination:last-child .item { + border-top: 1px solid #aaa; + } + +#nav_entries { + display: table; + width: 250px; + height: 40px; + position: fixed; + bottom: 0; + left: 0; + margin: 0; + background: #fff; + border-top: 1px solid #ddd; + text-align: center; + line-height: 40px; + table-layout: fixed; +} + #nav_entries .item { + display: table-cell; + width: 30%; + } + #nav_entries a { + display: block; + } + #nav_entries .i_up { + margin: 5px 0 0; + vertical-align: top; + } + +.loading { + background: url("loader.gif") center center no-repeat; + font-size: 0; +} + +#bigMarkAsRead { + display: block; + font-style: normal; + padding: 32px 0 64px 0; + text-align: center; + text-decoration: none; + text-shadow: 0 -1px 0 #aaa; +} + #bigMarkAsRead:hover { + background: #333; + color: #fff; + } + .bigTick { + font-size: 72pt; + line-height: 1.6em; + } + +/*** NOTIFICATION ***/ +.notification { + position: absolute; + top: 10px; + left: 25%; right: 25%; + min-height: 30px; + padding: 10px; + line-height: 30px; + text-align: center; + border-radius: 5px; + box-shadow: 0 0 5px #666; + background: #ddd; + color: #666; + font-weight: bold; + z-index: 10; +} + .notification.good { + background: #f4f899; + } + .notification.bad { + background: #f4a899; + } + .notification a.close { + display: inline-block; + width: 16px; + height: 16px; + float: right; + margin: -20px -20px 0 0; + padding: 5px; + background: #fff; + border-radius: 50px; + border: 1px solid #aaa; + line-height: 16px; + } + +.toggle_aside, .btn.toggle_aside { + display: none; +} + +.actualizeProgress { + position: fixed; + top: 10px; + left: 25%; right: 25%; + padding: 5px; + background: #fff; + text-align: center; + border: 1px solid #ddd; + border-radius: 5px; +} + .actualizeProgress progress { + max-width: 100%; + vertical-align: middle; + } + .actualizeProgress .progress { + color: #999; + font-size: 90%; + vertical-align: middle; + } + +.logs { + border: 1px solid #aaa; +} + .log { + padding: 5px 2%; + overflow: auto; + background: #fafafa; + border-bottom: 1px solid #999; + color: #333; + font-size: 90%; + } + .log .date { + display: block; + } + .log.error { + background: #fdd; + color: #844; + } + .log.warning { + background: #ffe; + color: #c95; + } + .log.notice { + background: #f4f4f4; + color: #aaa; + } + .log.debug { + background: #111; + color: #eee; + } + +.form-group table { + border-collapse:collapse; + margin:10px 0 0 220px; + text-align:center; +} + +.form-group tr, .form-group th, .form-group td { + border:1px solid #DDD; + font-weight:normal; + padding:.5em; +} + +select.number option { + text-align:right; +} + +@media(max-width: 840px) { + .header, + .aside .btn-important, + .aside .feeds .dropdown, + .flux_header .item.website span, + .item.date { + display: none; + } + .flux_header .item.website { + width: 40px; + text-align: center; + } + .flux_header .item.website .favicon { + padding: 12px; + } + + .nav-login { + display: block; + } + + .content { + font-size: 120%; + padding: 0; + } + + .pagination { + margin: 0 0 40px; + } + .pagination .pager-previous, .pagination .pager-next { + width: 100px; + } + + .toggle_aside, .btn.toggle_aside { + display: inline-block; + } + .aside { + position: fixed; + top: 0; left: 0; + width: 0; + overflow: hidden; + border-right: none; + z-index: 10; + transition: width 200ms linear; + } + .aside.aside_flux { + padding: 10px 0 0; + } + .aside:target { + width: 80%; + border-right: 1px solid #aaa; + overflow: auto; + } + .aside .toggle_aside { + position: absolute; + right: 0; + display: inline-block; + width: 26px; + height: 26px; + margin: 0 10px 0 0; + border: 1px solid #ccc; + border-radius: 20px; + text-align: center; + line-height: 26px; + } + .aside .categories { + margin: 30px 0; + } + + #nav_entries { + width: 100%; + } + + .nav_menu .btn { + margin: 5px 10px; + } + .nav_menu .stick { + margin: 0 10px; + } + .nav_menu .stick .btn { + margin: 5px 0; + } + .nav_menu .search { + display: inline-block; + max-width: 97%; + } + .nav_menu .search input { + max-width: 97%; + width: 90px; + } + .nav_menu .search input:focus { + width: 400px; + } + + #panel { + left: 5px; right: 5px; + } + + .day .date { + display: none; + } + .day .name { + height: 2.6em; + font-size: 1em; + text-shadow: none; + } + + .notification, + .actualizeProgress { + left: 10px; + right: 10px; + } +} + +/*** FALLBACK ***/ +.btn { + background: #fff; + background: -moz-linear-gradient(top, #fff 0%, #eee 100%); + background: -webkit-linear-gradient(top, #fff 0%, #eee 100%); + background: -o-linear-gradient(top, #fff 0%, #eee 100%); + background: -ms-linear-gradient(top, #fff 0%, #eee 100%); +} + .btn:hover { + background: #f0f0f0; + background: -moz-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); + background: -webkit-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); + background: -o-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); + background: -ms-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); + } + .btn-important { + background: #0084CC; + background: -moz-linear-gradient(top, #0084CC 0%, #0045CC 100%); + background: -webkit-linear-gradient(top, #0084CC 0%, #0045CC 100%); + background: -o-linear-gradient(top, #0084CC 0%, #0045CC 100%); + background: -ms-linear-gradient(top, #0084CC 0%, #0045CC 100%); + } + .btn-important:hover { + background: -moz-linear-gradient(top, #0066CC 0%, #0045CC 100%); + background: -webkit-linear-gradient(top, #0066CC 0%, #0045CC 100%); + background: -o-linear-gradient(top, #0066CC 0%, #0045CC 100%); + background: -ms-linear-gradient(top, #0066CC 0%, #0045CC 100%); + } + .btn-attention { + background: #E95B57; + background: -moz-linear-gradient(top, #E95B57 0%, #BD362F 100%); + background: -webkit-linear-gradient(top, #E95B57 0%, #BD362F 100%); + background: -o-linear-gradient(top, #E95B57 0%, #BD362F 100%); + background: -ms-linear-gradient(top, #E95B57 0%, #BD362F 100%); + } + .btn-attention:hover { + background: -moz-linear-gradient(top, #D14641 0%, #BD362F 100%); + background: -webkit-linear-gradient(top, #D14641 0%, #BD362F 100%); + background: -o-linear-gradient(top, #D14641 0%, #BD362F 100%); + background: -ms-linear-gradient(top, #D14641 0%, #BD362F 100%); + } + +.dropdown-menu:after { + -moz-transform: rotate(45deg); + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); +} + +.nav-head { + background: #fff; + background: -moz-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -webkit-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -o-linear-gradient(top, #fff 0%, #f0f0f0 100%); + background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%); +} + +.header > .item.search input { + -moz-transition: width 200ms linear; + -webkit-transition: width 200ms linear; + -o-transition: width 200ms linear; + -ms-transition: width 200ms linear; +} + +@media(max-width: 840px) { + .aside { + -moz-transition: width 200ms linear; + -webkit-transition: width 200ms linear; + -o-transition: width 200ms linear; + -ms-transition: width 200ms linear; + } +} + +@media print { + .header, + .aside, + .nav_menu, + .day, + .flux_header, + .flux_content .bottom, + .pagination { + display: none; + } + + html, body { + background: #fff; + color: #000; + font-family: Serif; + font-size: 12pt; + } + + #global, + .flux_content { + display: block !important; + } + + .flux_content .content { + width: 100% !important; + text-align: justify; + } + + .flux_content .content a { + color: #000; + } + .flux_content .content a:after { + content: " (" attr(href) ") "; + text-decoration: underline; + } +} diff --git a/p/themes/Origine/global.css b/p/themes/Origine/global.css new file mode 100644 index 000000000..49f3aa4cd --- /dev/null +++ b/p/themes/Origine/global.css @@ -0,0 +1,503 @@ +@charset "UTF-8"; + +/* FONTS */ +@font-face { + font-family: "OpenSans"; + src: url("../fonts/openSans.woff") format("woff"); +} + + +* { + margin: 0; + padding: 0; +} +html, body { + height: 100%; + font-size: 95%; + font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif; +} + +/* LIENS */ +a { + color: #0062BE; + text-decoration: none; +} + a:hover { + text-decoration: underline; + } + +/* LISTES */ +ul, ol, dl { + margin: 10px 0 10px 30px; + line-height: 190%; +} + dd { + margin: 0 0 10px 30px; + } + +/* TITRES */ +h1, h2, h3 { + min-height: 40px; + margin: 15px 0 5px; + line-height: 40px; +} + +/* IMG */ +figure { + margin: 5px 0 10px; + text-align: center; +} + figcaption { + display: inline-block; + padding: 3px 20px; + color: #999; + font-style: italic; + border-bottom: 1px solid #ccc; + } +img { + height: auto; + max-width: 100%; + vertical-align: middle; +} + a img { + border: none; + } + +/* VIDEOS */ +iframe, embed, object, video { + max-width: 100%; +} + +/* FORMULAIRES */ +legend { + display: block; + width: 100%; + margin: 20px 0 5px; + padding: 5px 0; + border-bottom: 1px solid #ddd; + font-size: 150%; + clear: both; +} +label { + display: block; + min-height: 25px; + padding: 5px 0; + font-size: 14px; + line-height: 25px; + cursor: pointer; +} +input, select, textarea { + display: inline-block; + max-width: 100%; + min-height: 25px; + padding: 5px; + background: #fdfdfd; + border: 1px solid #bbb; + border-radius: 3px; + color: #666; + line-height: 25px; + vertical-align: middle; + box-shadow: 0 2px 2px #eee inset; +} + option { + padding:0 .5em 0 .5em; + } + input[type="radio"], + input[type="checkbox"] { + width: 15px !important; + min-height: 15px !important; + } + input:focus, select:focus, textarea:focus { + color: #0062BE; + border-color: #33BBFF; + box-shadow: 0 2px 2px #DDDDFF inset; + } + input:invalid, select:invalid { + border-color: red; + box-shadow: 0 0 2px 1px red; + } + +.form-group { + margin: 0; +} + .form-group:after { + content: ""; + display: block; + clear: both; + } + .form-group.form-actions { + min-width: 250px; + padding: 5px 0; + background: #f4f4f4; + border-top: 1px solid #ddd; + } + .form-group.form-actions .btn { + margin: 0 10px; + } + .form-group .group-name { + display: block; + float: left; + width: 200px; + padding: 10px 0; + text-align: right; + } + .form-group .group-controls { + min-width: 250px; + min-height: 25px; + margin: 0 0 0 220px; + padding: 5px 0; + } + .form-group .group-controls .control { + display: block; + min-height: 30px; + padding: 5px 0; + line-height: 25px; + font-size: 14px; + } + +.stick { + display: inline-block; + white-space: nowrap; + font-size: 0px; + vertical-align: middle; +} + .stick input, + .stick .btn { + border-radius: 0; + font-size: 14px; + } + .stick .btn:first-child, + .stick input:first-child { + border-radius: 3px 0 0 3px; + } + .stick .btn-important:first-child { + border-right: 1px solid #06f; + } + .stick .btn:last-child, + .stick input:last-child { + border-radius: 0 3px 3px 0; + } + .stick .btn + .btn, + .stick .btn + input, + .stick input + .btn, + .stick input + input { + border-left: none; + } + .stick input + .btn { + border-top: 1px solid #bbb; + } + .stick .btn + .dropdown > .btn { + border-left: none; + border-radius: 0 3px 3px 0; + } + .stick .btn + .dropdown a { + font-size: 12px; + } + +.btn { + display: inline-block; + min-height: 37px; + min-width: 15px; + padding: 5px 10px; + background: linear-gradient(to bottom, #fff 0%, #eee 100%); + border-radius: 3px; + border: 1px solid #ddd; + border-bottom: 1px solid #aaa; + border-right: 1px solid #aaa; + color: #666; + text-shadow: 0px -1px 0 #ddd; + line-height: 20px; + vertical-align: middle; + cursor: pointer; + overflow: hidden; +} + a.btn { + min-height: 25px; + line-height: 25px; + } + .btn:hover { + background: linear-gradient(to bottom, #f8f8f8, #f0f0f0); + text-decoration: none; + } + .btn.active, + .btn:active, + .dropdown-target:target ~ .btn.dropdown-toggle { + box-shadow: 0px 2px 4px #e0e0e0 inset, 0px 1px 2px #fafafa; + background: #eee; + } + + .btn-important { + background: linear-gradient(to bottom, #0084CC, #0045CC); + color: #fff; + border: 1px solid #0062B7; + text-shadow: 0px -1px 0 #aaa; + } + .btn-important:hover { + background: linear-gradient(to bottom, #0066CC, #0045CC); + } + .btn-important:active { + background: #0044CB; + box-shadow: none; + } + + .btn-attention { + background: linear-gradient(to bottom, #E95B57, #BD362F); + color: #fff; + border: 1px solid #C44742; + text-shadow: 0px -1px 0px #666; + } + .btn-attention:hover { + background: linear-gradient(to bottom, #D14641, #BD362F); + } + .btn-attention:active { + background: #BD362F; + box-shadow: none; + } + +/* NAVIGATION */ +.nav-list .nav-header, +.nav-list .item { + display: block; + height: 35px; + line-height: 35px; +} + .nav-list .item:hover { + background: #fafafa; + } + .nav-list .item:hover a { + color: #003388; + } + .nav-list .item.active { + background: #0062BE; + color: #fff; + } + .nav-list .item.active a { + color: #fff; + } + .nav-list .disable { + color: #aaa; + background: #fafafa; + text-align: center; + } + .nav-list .item > * { + display: block; + padding: 0 10px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .nav-list a:hover { + text-decoration: none; + } + .nav-list .item.error a { + color: #BD362F; + } + .nav-list .item.active.error a { + color: #fff; + background: #BD362F; + } + .nav-list .item.empty a { + color: #f39c12; + } + .nav-list .item.active.empty a { + color: #fff; + background: #f39c12; + } + + .nav-list .nav-header { + padding: 0 10px; + color: #888; + background: #f4f4f4; + border-bottom: 1px solid #ddd; + font-weight: bold; + text-shadow: 0 0 1px #ddd; + } + .nav-list .separator { + display: block; + height: 0; + margin: 5px 0; + border-bottom: 1px solid #ddd; + } + + .nav-list .nav-form { + padding: 3px; + text-align: center; + } + +.nav-head { + display: block; + margin: 0; + background: linear-gradient(to bottom, #fff, #f0f0f0); + border-bottom: 1px solid #ddd; + text-align: right; +} + .nav-head .item { + display: inline-block; + padding: 5px 10px; + } + +/* HORIZONTAL-LIST */ +.horizontal-list { + display: table; + table-layout: fixed; + margin: 0; + padding: 0; + width: 100%; +} + .horizontal-list .item { + display: table-cell; + vertical-align: middle; + } + +/* DROPDOWN */ +.dropdown { + position: relative; + display: inline-block; +} + .dropdown-target { + display: none; + } + + .dropdown-menu { + display: none; + min-width: 200px; + margin: 5px 0 0; + padding: 5px 0; + position: absolute; + right: 0px; + background: #fff; + border: 1px solid #ddd; + border-radius: 5px; + text-align: left; + box-shadow: 3px 3px 3px #ddd; + } + .dropdown-menu:after { + content: ""; + position: absolute; + top: -6px; + right: 13px; + width: 10px; + height: 10px; + background: #fff; + border-top: 1px solid #ddd; + border-left: 1px solid #ddd; + z-index: -10; + transform: rotate(45deg); + } + .dropdown-header { + display: block; + padding: 0 5px; + color: #888; + font-weight: bold; + font-size: 14px; + line-height: 30px; + } + .dropdown-menu > .item { + display: block; + height: 30px; + font-size: 90%; + line-height: 30px; + } + .dropdown-menu > .item > a { + display: block; + padding: 0 25px; + line-height: 30px; + } + .dropdown-menu > .item:hover { + background: #0062BE; + color: #fff; + } + .dropdown-menu > .item[aria-checked="true"] > a:before { + content: '✓ '; + font-weight: bold; + margin: 0 0 0 -1.2em; + padding: 0 0.2em 0 0; + } + .dropdown-menu > .item:hover > a { + color: #fff; + text-decoration: none; + } + .dropdown-menu .input { + display: block; + height: 40px; + font-size: 90%; + line-height: 30px; + } + .dropdown-menu .input select, + .dropdown-menu .input input { + display: block; + height: 20px; + width: 95%; + margin: auto; + padding: 2px 5px; + border-radius: 3px; + } + .dropdown-menu .input select { + width: 70%; + height: auto; + } + .dropdown-menu .separator { + display: block; + height: 0; + margin: 5px 0; + border-bottom: 1px solid #ddd; + } + .dropdown-target:target ~ .dropdown-menu { + display: block; + z-index: 10; + } + .dropdown-close { + display: inline; + } + .dropdown-close a { + font-size: 0; + position: fixed; + top: 0; bottom: 0; + left: 0; right: 0; + display: block; + z-index: -10; + } + +/* ALERTS */ +.alert { + display: block; + width: 90%; + margin: 15px auto; + padding: 10px 15px; + background: #f4f4f4; + border: 1px solid #ccc; + border-right: 1px solid #aaa; + border-bottom: 1px solid #aaa; + border-radius: 5px; + color: #aaa; + text-shadow: 0 0 1px #eee; +} + .alert-head { + margin: 0; + font-weight: bold; + font-size: 110%; + } + .alert-warn { + background: #ffe; + border: 1px solid #eeb; + color: #c95; + } + .alert-success { + background: #dfd; + border: 1px solid #cec; + color: #484; + } + .alert-error { + background: #fdd; + border: 1px solid #ecc; + color: #844; + } + +/* ICÔNES */ +.icon { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + line-height: 16px; +} diff --git a/p/themes/Origine/loader.gif b/p/themes/Origine/loader.gif new file mode 100644 index 000000000..5ff26f0e3 Binary files /dev/null and b/p/themes/Origine/loader.gif differ diff --git a/p/themes/Origine/metadata.json b/p/themes/Origine/metadata.json new file mode 100644 index 000000000..f93dcbc3f --- /dev/null +++ b/p/themes/Origine/metadata.json @@ -0,0 +1,7 @@ +{ + "name": "Origine", + "author": "Marien Fressinaud", + "description": "Le thème par défaut pour FreshRSS", + "version": 0.1, + "files": ["global.css", "freshrss.css"] +} diff --git a/p/themes/default/freshrss.css b/p/themes/default/freshrss.css deleted file mode 100644 index 593f21d30..000000000 --- a/p/themes/default/freshrss.css +++ /dev/null @@ -1,880 +0,0 @@ -@charset "UTF-8"; - -/* STRUCTURE */ -.header { - display: table; - width: 100%; - background: #f4f4f4; - table-layout: fixed; -} - .header > .item { - display: table-cell; - padding: 10px 0; - border-bottom: 1px solid #aaa; - vertical-align: middle; - text-align: center; - } - .header > .item.title { - width: 250px; - white-space: nowrap; - } - .logo { - display: inline-block; - font-size: 48px; - height: 32px; - width: 32px; - padding: 10px; - } - .header > .item.title h1 { - display: inline-block; - margin: 0; - text-shadow: 1px -1px 0 #ccc; - } - .header > .item.search input { - width: 230px; - transition: width 200ms linear; - } - .header .item.search input:focus { - width: 330px; - } - .header > .item.configure { - width: 100px; - } - -.item a:hover { - text-decoration: none; -} - -#global { - display: table; - width: 100%; - height: 100%; - background: #fafafa; - table-layout: fixed; -} - .aside { - display: table-cell; - height: 100%; - width: 250px; - vertical-align: top; - border-right: 1px solid #aaa; - background: #fff; - } - .aside .nav-form input { - width: 180px; - } - .aside.aside_flux { - padding: 10px 0 40px; - } - .aside.aside_feed .nav-form input { - width: 140px; - } - .aside.aside_feed .nav-form .dropdown .dropdown-menu { - right: -20px; - } - .aside.aside_feed .nav-form .dropdown .dropdown-menu:after { - right: 33px; - } - - .nav-login { - display: none; - } - - .nav_menu { - width: 100%; - background: #fafafa; - border-bottom: 1px solid #aaa; - text-align: center; - padding: 5px 0; - } - .nav_menu .search { - display:none; - } - -.favicon { - height: 16px; - width: 16px; -} - -.categories { - margin: 0; - padding: 0; - text-align: center; - list-style: none; -} - .category { - display: block; - width: 220px; - margin: 10px auto; - text-align: left; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .category .btn:first-child { - width: 195px; - position: relative; - } - .category.stick .btn:first-child { - width:160px; - } - .category .btn:first-child:not([data-unread="0"]):after { - content: attr(data-unread); - position: absolute; - top: 3px; right: 3px; - padding: 1px 5px; - background: #ccc; - color: #fff; - font-size: 90%; - border: 1px solid #bbb; - border-radius: 5px; - box-shadow: 1px 3px 3px #aaa inset; - text-shadow: 0 0 1px #aaa; - } - .category + .feeds:not(.active) { - display:none; - } - .categories .feeds { - width: 100%; - margin: 0; - list-style: none; - } - .categories .feeds .item.active { - background: #0062BE; - } - .categories .feeds .item.active .feed { - color: #fff; - } - .categories .feeds .item.empty .feed { - color: #e67e22; - } - .categories .feeds .item.empty.active { - background: #e67e22; - } - .categories .feeds .item.empty.active .feed { - color: #fff; - } - .categories .feeds .item.error .feed { - color: #BD362F; - } - .categories .feeds .item .feed { - display: inline-block; - margin: 0; - width: 165px; - line-height: 35px; - font-size: 90%; - vertical-align: middle; - text-align: left; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .feed:not([data-unread="0"]) { - font-weight:bold; - } - .feed:not([data-unread="0"]):before { - content: "(" attr(data-unread) ") "; - } - .categories .feeds .dropdown-menu { - left: 0; - } - .categories .feeds .dropdown-menu:after { - left: 2px; - } - .categories .feeds .item .dropdown-toggle > .icon { - visibility: hidden; - cursor: pointer; - } - .categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, - .categories .feeds .item:hover .dropdown-toggle > .icon, - .categories .feeds .item.active .dropdown-toggle > .icon { - background-color: #fff; - border-radius: 3px; - visibility: visible; - } - -.post { - padding: 10px 50px; -} - .post form { - margin: 10px 0; - } - -.day { - min-height: 50px; - padding: 0 10px; - font-size: 130%; - font-weight: bold; - line-height: 50px; - background: #fff; - border-top: 1px solid #aaa; - border-bottom: 1px solid #aaa; -} - .day:first-child { - border-top: none; - } - .day .name { - position: absolute; - right: 0; - width: 50%; - height: 1.5em; - padding: 0 10px 0 0; - overflow: hidden; - color: #aab; - font-size: 1.8em; - opacity: .3; - text-shadow: 0px -1px 0px #333; - font-style: italic; - white-space: nowrap; - text-overflow: ellipsis; - text-align: right; - } - -.flux { - border-left: 3px solid #aaa; - background: #fafafa; -} - .flux:hover { - background: #fff; - } - .flux.not_read { - border-left: 3px solid #FF5300; - background: #FFF3ED; - } - .flux.favorite { - border-left: 3px solid #FFC300; - background: #FFF6DA; - } - .flux.current { - border-left: 3px solid #0062BE; - background: #fff; - } - - .flux_header { - background: inherit; - height: 25px; - font-size: 12px; - border-top: 1px solid #ddd; - cursor: pointer; - } - .flux .item { - line-height: 40px; - white-space: nowrap; - } - .flux_header > .item { - overflow: hidden; - text-overflow: ellipsis; - } - .flux .item.manage { - width: 40px; - text-align: center; - } - .flux .item.website { - width: 200px; - } - .website .favicon { - padding: 5px; - } - .flux .item.title { - background: inherit; - } - .flux:hover .item.title { - border-right: 2px solid rgba(127, 127, 127, 0.1); - padding-right: 1em; - position: absolute; - } - .flux .item.title a { - color: #000; - outline: none; - } - .flux.not_read .item.title, - .flux.current .item.title { - font-weight: bold; - } - .flux .item.date { - width: 200px; - padding:0 5px 0 0; - text-align: right; - font-size: 10px; - color: #666; - } - .link { - width: 40px; - text-align: center; - } - -#stream.reader .flux { - padding: 0 0 30px; - border: none; - background: #f0f0f0; - color: #333; -} - #stream.reader .flux .author { - margin: 0 0 10px; - font-size: 90%; - color: #666; - } - -#stream.global { - text-align: center; -} - #stream.global .box-category { - display: inline-block; - width: 280px; - margin: 20px 10px; - vertical-align: top; - background: #fff; - border: 1px solid #aaa; - border-radius: 5px; - text-align: left; - box-shadow: 0 0 5px #bbb; - } - #stream.global .category { - width: 100%; - margin: 0; - } - #stream.global .btn { - display: block; - width: auto; - height: 35px; - margin: 0; - padding: 0 10px; - background: #eee; - border: none; - border-bottom: 1px solid #aaa; - border-radius: 5px 5px 0 0; - line-height: 35px; - font-size: 120%; - } - #stream.global .btn:not([data-unread="0"]) { - font-weight:bold; - } - #stream.global .btn:first-child:not([data-unread="0"]):after { - top: 0; right: 5px; - border: 0; - background: none; - color: #666; - font-weight: bold; - box-shadow: none; - } - #stream.global .box-category .feeds { - display: block; - max-height: 250px; - margin: 0; - list-style: none; - overflow: auto; - } - #stream.global .box-category .feeds .item { - padding: 2px 10px; - font-size: 90%; - } - #stream.global .box-category .feed { - width: 220px; - } - -.content { - min-height: 150px; - max-width: 550px; - margin: 0 auto; - padding: 20px 10px; - line-height: 170%; - word-wrap: break-word; -} - .content h1, .content h2, .content h3 { - margin: 20px 0 5px; - } - .content > .title { - font-size: x-large; - margin: 0; - } - - .content p { - margin: 0 0 20px; - } - img.big { - display: block; - margin: 10px auto; - } - figure img.big { - margin: 0; - } - .content hr { - margin: 30px 0; - height: 1px; - background: #ddd; - border: 0; - } - .content pre { - margin: 10px auto; - padding: 10px; - overflow: auto; - background: #000; - color: #fff; - font-size: 110%; - } - .content q, .content blockquote { - display: block; - margin: 5px 0; - padding: 5px 20px; - font-style: italic; - border-left: 4px solid #ccc; - color: #666; - } - .content blockquote p { - margin: 0; - } - -#panel { - display: none; - position: fixed; - top: 10px; bottom: 10px; - left: 100px; right: 100px; - overflow: auto; - background: #fff; - border: 1px solid #95a5a6; - border-radius: 5px; -} - #panel .close { - position: fixed; - top: 10px; right: 0; - display: inline-block; - width: 26px; - height: 26px; - margin: 0 10px 0 0; - border: 1px solid #ccc; - border-radius: 20px; - text-align: center; - line-height: 26px; - background: #fff; - } - -#overlay { - display: none; - position: fixed; - top: 0; bottom: 0; - left: 0; right: 0; - background: rgba(0, 0, 0, 0.9); -} - -.flux_content .bottom { - font-size: 90%; - text-align: center; -} - -.hide_posts > :not(.active) > .flux_content { - display:none; -} - -/*** PAGINATION ***/ -.pagination { - display: table; - width: 100%; - margin: 0; - background: #fafafa; - text-align: center; - color: #333; - font-size: 80%; - line-height: 200%; - table-layout: fixed; -} - .pagination .item { - display: table-cell; - line-height: 40px; - } - .pagination .item.pager-current { - font-weight: bold; - font-size: 140%; - } - .pagination .pager-first, - .pagination .pager-previous, - .pagination .pager-next, - .pagination .pager-last { - width: 100px; - } - .pagination .item a { - display: block; - color: #333; - font-style: italic; - } - .pagination:first-child .item { - border-bottom: 1px solid #aaa; - } - .pagination:last-child .item { - border-top: 1px solid #aaa; - } - -#nav_entries { - display: table; - width: 250px; - height: 40px; - position: fixed; - bottom: 0; - left: 0; - margin: 0; - background: #fff; - border-top: 1px solid #ddd; - text-align: center; - line-height: 40px; - table-layout: fixed; -} - #nav_entries .item { - display: table-cell; - width: 30%; - } - #nav_entries a { - display: block; - } - #nav_entries .i_up { - margin: 5px 0 0; - vertical-align: top; - } - -.loading { - background: url("loader.gif") center center no-repeat; - font-size: 0; -} - -#bigMarkAsRead { - display: block; - font-style: normal; - padding: 32px 0 64px 0; - text-align: center; - text-decoration: none; - text-shadow: 0 -1px 0 #aaa; -} - #bigMarkAsRead:hover { - background: #333; - color: #fff; - } - .bigTick { - font-size: 72pt; - line-height: 1.6em; - } - -/*** NOTIFICATION ***/ -.notification { - position: absolute; - top: 10px; - left: 25%; right: 25%; - min-height: 30px; - padding: 10px; - line-height: 30px; - text-align: center; - border-radius: 5px; - box-shadow: 0 0 5px #666; - background: #ddd; - color: #666; - font-weight: bold; - z-index: 10; -} - .notification.good { - background: #f4f899; - } - .notification.bad { - background: #f4a899; - } - .notification a.close { - display: inline-block; - width: 16px; - height: 16px; - float: right; - margin: -20px -20px 0 0; - padding: 5px; - background: #fff; - border-radius: 50px; - border: 1px solid #aaa; - line-height: 16px; - } - -.toggle_aside, .btn.toggle_aside { - display: none; -} - -.actualizeProgress { - position: fixed; - top: 10px; - left: 25%; right: 25%; - padding: 5px; - background: #fff; - text-align: center; - border: 1px solid #ddd; - border-radius: 5px; -} - .actualizeProgress progress { - max-width: 100%; - vertical-align: middle; - } - .actualizeProgress .progress { - color: #999; - font-size: 90%; - vertical-align: middle; - } - -.logs { - border: 1px solid #aaa; -} - .log { - padding: 5px 2%; - overflow: auto; - background: #fafafa; - border-bottom: 1px solid #999; - color: #333; - font-size: 90%; - } - .log .date { - display: block; - } - .log.error { - background: #fdd; - color: #844; - } - .log.warning { - background: #ffe; - color: #c95; - } - .log.notice { - background: #f4f4f4; - color: #aaa; - } - .log.debug { - background: #111; - color: #eee; - } - -.form-group table { - border-collapse:collapse; - margin:10px 0 0 220px; - text-align:center; -} - -.form-group tr, .form-group th, .form-group td { - border:1px solid #DDD; - font-weight:normal; - padding:.5em; -} - -select.number option { - text-align:right; -} - -@media(max-width: 840px) { - .header, - .aside .btn-important, - .aside .feeds .dropdown, - .flux_header .item.website span, - .item.date { - display: none; - } - .flux_header .item.website { - width: 40px; - text-align: center; - } - .flux_header .item.website .favicon { - padding: 12px; - } - - .nav-login { - display: block; - } - - .content { - font-size: 120%; - padding: 0; - } - - .pagination { - margin: 0 0 40px; - } - .pagination .pager-previous, .pagination .pager-next { - width: 100px; - } - - .toggle_aside, .btn.toggle_aside { - display: inline-block; - } - .aside { - position: fixed; - top: 0; left: 0; - width: 0; - overflow: hidden; - border-right: none; - z-index: 10; - transition: width 200ms linear; - } - .aside.aside_flux { - padding: 10px 0 0; - } - .aside:target { - width: 80%; - border-right: 1px solid #aaa; - overflow: auto; - } - .aside .toggle_aside { - position: absolute; - right: 0; - display: inline-block; - width: 26px; - height: 26px; - margin: 0 10px 0 0; - border: 1px solid #ccc; - border-radius: 20px; - text-align: center; - line-height: 26px; - } - .aside .categories { - margin: 30px 0; - } - - #nav_entries { - width: 100%; - } - - .nav_menu .btn { - margin: 5px 10px; - } - .nav_menu .stick { - margin: 0 10px; - } - .nav_menu .stick .btn { - margin: 5px 0; - } - .nav_menu .search { - display: inline-block; - max-width: 97%; - } - .nav_menu .search input { - max-width: 97%; - width: 90px; - } - .nav_menu .search input:focus { - width: 400px; - } - - #panel { - left: 5px; right: 5px; - } - - .day .date { - display: none; - } - .day .name { - height: 2.6em; - font-size: 1em; - text-shadow: none; - } - - .notification, - .actualizeProgress { - left: 10px; - right: 10px; - } -} - -/*** FALLBACK ***/ -.btn { - background: #fff; - background: -moz-linear-gradient(top, #fff 0%, #eee 100%); - background: -webkit-linear-gradient(top, #fff 0%, #eee 100%); - background: -o-linear-gradient(top, #fff 0%, #eee 100%); - background: -ms-linear-gradient(top, #fff 0%, #eee 100%); -} - .btn:hover { - background: #f0f0f0; - background: -moz-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); - background: -webkit-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); - background: -o-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); - background: -ms-linear-gradient(top, #f8f8f8 0%, #f0f0f0 100%); - } - .btn-important { - background: #0084CC; - background: -moz-linear-gradient(top, #0084CC 0%, #0045CC 100%); - background: -webkit-linear-gradient(top, #0084CC 0%, #0045CC 100%); - background: -o-linear-gradient(top, #0084CC 0%, #0045CC 100%); - background: -ms-linear-gradient(top, #0084CC 0%, #0045CC 100%); - } - .btn-important:hover { - background: -moz-linear-gradient(top, #0066CC 0%, #0045CC 100%); - background: -webkit-linear-gradient(top, #0066CC 0%, #0045CC 100%); - background: -o-linear-gradient(top, #0066CC 0%, #0045CC 100%); - background: -ms-linear-gradient(top, #0066CC 0%, #0045CC 100%); - } - .btn-attention { - background: #E95B57; - background: -moz-linear-gradient(top, #E95B57 0%, #BD362F 100%); - background: -webkit-linear-gradient(top, #E95B57 0%, #BD362F 100%); - background: -o-linear-gradient(top, #E95B57 0%, #BD362F 100%); - background: -ms-linear-gradient(top, #E95B57 0%, #BD362F 100%); - } - .btn-attention:hover { - background: -moz-linear-gradient(top, #D14641 0%, #BD362F 100%); - background: -webkit-linear-gradient(top, #D14641 0%, #BD362F 100%); - background: -o-linear-gradient(top, #D14641 0%, #BD362F 100%); - background: -ms-linear-gradient(top, #D14641 0%, #BD362F 100%); - } - -.dropdown-menu:after { - -moz-transform: rotate(45deg); - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); -} - -.nav-head { - background: #fff; - background: -moz-linear-gradient(top, #fff 0%, #f0f0f0 100%); - background: -webkit-linear-gradient(top, #fff 0%, #f0f0f0 100%); - background: -o-linear-gradient(top, #fff 0%, #f0f0f0 100%); - background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%); -} - -.header > .item.search input { - -moz-transition: width 200ms linear; - -webkit-transition: width 200ms linear; - -o-transition: width 200ms linear; - -ms-transition: width 200ms linear; -} - -@media(max-width: 840px) { - .aside { - -moz-transition: width 200ms linear; - -webkit-transition: width 200ms linear; - -o-transition: width 200ms linear; - -ms-transition: width 200ms linear; - } -} - -@media print { - .header, - .aside, - .nav_menu, - .day, - .flux_header, - .flux_content .bottom, - .pagination { - display: none; - } - - html, body { - background: #fff; - color: #000; - font-family: Serif; - font-size: 12pt; - } - - #global, - .flux_content { - display: block !important; - } - - .flux_content .content { - width: 100% !important; - text-align: justify; - } - - .flux_content .content a { - color: #000; - } - .flux_content .content a:after { - content: " (" attr(href) ") "; - text-decoration: underline; - } -} diff --git a/p/themes/default/global.css b/p/themes/default/global.css deleted file mode 100644 index 49f3aa4cd..000000000 --- a/p/themes/default/global.css +++ /dev/null @@ -1,503 +0,0 @@ -@charset "UTF-8"; - -/* FONTS */ -@font-face { - font-family: "OpenSans"; - src: url("../fonts/openSans.woff") format("woff"); -} - - -* { - margin: 0; - padding: 0; -} -html, body { - height: 100%; - font-size: 95%; - font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif; -} - -/* LIENS */ -a { - color: #0062BE; - text-decoration: none; -} - a:hover { - text-decoration: underline; - } - -/* LISTES */ -ul, ol, dl { - margin: 10px 0 10px 30px; - line-height: 190%; -} - dd { - margin: 0 0 10px 30px; - } - -/* TITRES */ -h1, h2, h3 { - min-height: 40px; - margin: 15px 0 5px; - line-height: 40px; -} - -/* IMG */ -figure { - margin: 5px 0 10px; - text-align: center; -} - figcaption { - display: inline-block; - padding: 3px 20px; - color: #999; - font-style: italic; - border-bottom: 1px solid #ccc; - } -img { - height: auto; - max-width: 100%; - vertical-align: middle; -} - a img { - border: none; - } - -/* VIDEOS */ -iframe, embed, object, video { - max-width: 100%; -} - -/* FORMULAIRES */ -legend { - display: block; - width: 100%; - margin: 20px 0 5px; - padding: 5px 0; - border-bottom: 1px solid #ddd; - font-size: 150%; - clear: both; -} -label { - display: block; - min-height: 25px; - padding: 5px 0; - font-size: 14px; - line-height: 25px; - cursor: pointer; -} -input, select, textarea { - display: inline-block; - max-width: 100%; - min-height: 25px; - padding: 5px; - background: #fdfdfd; - border: 1px solid #bbb; - border-radius: 3px; - color: #666; - line-height: 25px; - vertical-align: middle; - box-shadow: 0 2px 2px #eee inset; -} - option { - padding:0 .5em 0 .5em; - } - input[type="radio"], - input[type="checkbox"] { - width: 15px !important; - min-height: 15px !important; - } - input:focus, select:focus, textarea:focus { - color: #0062BE; - border-color: #33BBFF; - box-shadow: 0 2px 2px #DDDDFF inset; - } - input:invalid, select:invalid { - border-color: red; - box-shadow: 0 0 2px 1px red; - } - -.form-group { - margin: 0; -} - .form-group:after { - content: ""; - display: block; - clear: both; - } - .form-group.form-actions { - min-width: 250px; - padding: 5px 0; - background: #f4f4f4; - border-top: 1px solid #ddd; - } - .form-group.form-actions .btn { - margin: 0 10px; - } - .form-group .group-name { - display: block; - float: left; - width: 200px; - padding: 10px 0; - text-align: right; - } - .form-group .group-controls { - min-width: 250px; - min-height: 25px; - margin: 0 0 0 220px; - padding: 5px 0; - } - .form-group .group-controls .control { - display: block; - min-height: 30px; - padding: 5px 0; - line-height: 25px; - font-size: 14px; - } - -.stick { - display: inline-block; - white-space: nowrap; - font-size: 0px; - vertical-align: middle; -} - .stick input, - .stick .btn { - border-radius: 0; - font-size: 14px; - } - .stick .btn:first-child, - .stick input:first-child { - border-radius: 3px 0 0 3px; - } - .stick .btn-important:first-child { - border-right: 1px solid #06f; - } - .stick .btn:last-child, - .stick input:last-child { - border-radius: 0 3px 3px 0; - } - .stick .btn + .btn, - .stick .btn + input, - .stick input + .btn, - .stick input + input { - border-left: none; - } - .stick input + .btn { - border-top: 1px solid #bbb; - } - .stick .btn + .dropdown > .btn { - border-left: none; - border-radius: 0 3px 3px 0; - } - .stick .btn + .dropdown a { - font-size: 12px; - } - -.btn { - display: inline-block; - min-height: 37px; - min-width: 15px; - padding: 5px 10px; - background: linear-gradient(to bottom, #fff 0%, #eee 100%); - border-radius: 3px; - border: 1px solid #ddd; - border-bottom: 1px solid #aaa; - border-right: 1px solid #aaa; - color: #666; - text-shadow: 0px -1px 0 #ddd; - line-height: 20px; - vertical-align: middle; - cursor: pointer; - overflow: hidden; -} - a.btn { - min-height: 25px; - line-height: 25px; - } - .btn:hover { - background: linear-gradient(to bottom, #f8f8f8, #f0f0f0); - text-decoration: none; - } - .btn.active, - .btn:active, - .dropdown-target:target ~ .btn.dropdown-toggle { - box-shadow: 0px 2px 4px #e0e0e0 inset, 0px 1px 2px #fafafa; - background: #eee; - } - - .btn-important { - background: linear-gradient(to bottom, #0084CC, #0045CC); - color: #fff; - border: 1px solid #0062B7; - text-shadow: 0px -1px 0 #aaa; - } - .btn-important:hover { - background: linear-gradient(to bottom, #0066CC, #0045CC); - } - .btn-important:active { - background: #0044CB; - box-shadow: none; - } - - .btn-attention { - background: linear-gradient(to bottom, #E95B57, #BD362F); - color: #fff; - border: 1px solid #C44742; - text-shadow: 0px -1px 0px #666; - } - .btn-attention:hover { - background: linear-gradient(to bottom, #D14641, #BD362F); - } - .btn-attention:active { - background: #BD362F; - box-shadow: none; - } - -/* NAVIGATION */ -.nav-list .nav-header, -.nav-list .item { - display: block; - height: 35px; - line-height: 35px; -} - .nav-list .item:hover { - background: #fafafa; - } - .nav-list .item:hover a { - color: #003388; - } - .nav-list .item.active { - background: #0062BE; - color: #fff; - } - .nav-list .item.active a { - color: #fff; - } - .nav-list .disable { - color: #aaa; - background: #fafafa; - text-align: center; - } - .nav-list .item > * { - display: block; - padding: 0 10px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .nav-list a:hover { - text-decoration: none; - } - .nav-list .item.error a { - color: #BD362F; - } - .nav-list .item.active.error a { - color: #fff; - background: #BD362F; - } - .nav-list .item.empty a { - color: #f39c12; - } - .nav-list .item.active.empty a { - color: #fff; - background: #f39c12; - } - - .nav-list .nav-header { - padding: 0 10px; - color: #888; - background: #f4f4f4; - border-bottom: 1px solid #ddd; - font-weight: bold; - text-shadow: 0 0 1px #ddd; - } - .nav-list .separator { - display: block; - height: 0; - margin: 5px 0; - border-bottom: 1px solid #ddd; - } - - .nav-list .nav-form { - padding: 3px; - text-align: center; - } - -.nav-head { - display: block; - margin: 0; - background: linear-gradient(to bottom, #fff, #f0f0f0); - border-bottom: 1px solid #ddd; - text-align: right; -} - .nav-head .item { - display: inline-block; - padding: 5px 10px; - } - -/* HORIZONTAL-LIST */ -.horizontal-list { - display: table; - table-layout: fixed; - margin: 0; - padding: 0; - width: 100%; -} - .horizontal-list .item { - display: table-cell; - vertical-align: middle; - } - -/* DROPDOWN */ -.dropdown { - position: relative; - display: inline-block; -} - .dropdown-target { - display: none; - } - - .dropdown-menu { - display: none; - min-width: 200px; - margin: 5px 0 0; - padding: 5px 0; - position: absolute; - right: 0px; - background: #fff; - border: 1px solid #ddd; - border-radius: 5px; - text-align: left; - box-shadow: 3px 3px 3px #ddd; - } - .dropdown-menu:after { - content: ""; - position: absolute; - top: -6px; - right: 13px; - width: 10px; - height: 10px; - background: #fff; - border-top: 1px solid #ddd; - border-left: 1px solid #ddd; - z-index: -10; - transform: rotate(45deg); - } - .dropdown-header { - display: block; - padding: 0 5px; - color: #888; - font-weight: bold; - font-size: 14px; - line-height: 30px; - } - .dropdown-menu > .item { - display: block; - height: 30px; - font-size: 90%; - line-height: 30px; - } - .dropdown-menu > .item > a { - display: block; - padding: 0 25px; - line-height: 30px; - } - .dropdown-menu > .item:hover { - background: #0062BE; - color: #fff; - } - .dropdown-menu > .item[aria-checked="true"] > a:before { - content: '✓ '; - font-weight: bold; - margin: 0 0 0 -1.2em; - padding: 0 0.2em 0 0; - } - .dropdown-menu > .item:hover > a { - color: #fff; - text-decoration: none; - } - .dropdown-menu .input { - display: block; - height: 40px; - font-size: 90%; - line-height: 30px; - } - .dropdown-menu .input select, - .dropdown-menu .input input { - display: block; - height: 20px; - width: 95%; - margin: auto; - padding: 2px 5px; - border-radius: 3px; - } - .dropdown-menu .input select { - width: 70%; - height: auto; - } - .dropdown-menu .separator { - display: block; - height: 0; - margin: 5px 0; - border-bottom: 1px solid #ddd; - } - .dropdown-target:target ~ .dropdown-menu { - display: block; - z-index: 10; - } - .dropdown-close { - display: inline; - } - .dropdown-close a { - font-size: 0; - position: fixed; - top: 0; bottom: 0; - left: 0; right: 0; - display: block; - z-index: -10; - } - -/* ALERTS */ -.alert { - display: block; - width: 90%; - margin: 15px auto; - padding: 10px 15px; - background: #f4f4f4; - border: 1px solid #ccc; - border-right: 1px solid #aaa; - border-bottom: 1px solid #aaa; - border-radius: 5px; - color: #aaa; - text-shadow: 0 0 1px #eee; -} - .alert-head { - margin: 0; - font-weight: bold; - font-size: 110%; - } - .alert-warn { - background: #ffe; - border: 1px solid #eeb; - color: #c95; - } - .alert-success { - background: #dfd; - border: 1px solid #cec; - color: #484; - } - .alert-error { - background: #fdd; - border: 1px solid #ecc; - color: #844; - } - -/* ICÔNES */ -.icon { - display: inline-block; - width: 16px; - height: 16px; - vertical-align: middle; - line-height: 16px; -} diff --git a/p/themes/default/loader.gif b/p/themes/default/loader.gif deleted file mode 100644 index 5ff26f0e3..000000000 Binary files a/p/themes/default/loader.gif and /dev/null differ diff --git a/p/themes/default/metadata.json b/p/themes/default/metadata.json deleted file mode 100644 index d316ec517..000000000 --- a/p/themes/default/metadata.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Default", - "author": "Marien Fressinaud", - "description": "Le thème par défaut pour FreshRSS", - "version": 0.1, - "files": ["global.css", "freshrss.css"] -} diff --git a/p/themes/default_dark/freshrss.css b/p/themes/default_dark/freshrss.css deleted file mode 100644 index e9eb2c705..000000000 --- a/p/themes/default_dark/freshrss.css +++ /dev/null @@ -1,862 +0,0 @@ -@charset "UTF-8"; - -/* STRUCTURE */ -.header { - display: table; - width: 100%; - background: #1c1c1c; - table-layout: fixed; -} - .header > .item { - display: table-cell; - padding: 10px 0; - border-bottom: 1px solid #2f2f2f; - vertical-align: middle; - text-align: center; - } - .header > .item.title { - width: 250px; - white-space: nowrap; - } - .logo { - display: inline-block; - font-size: 48px; - height: 32px; - width: 32px; - padding: 10px; - } - .header > .item.title h1 { - display: inline-block; - margin: 0; - } - .header > .item.search input { - width: 230px; - transition: width 200ms linear; - } - .header .item.search input:focus { - width: 330px; - } - .header > .item.configure { - width: 100px; - } - -.item a:hover { - text-decoration: none; -} - -#global { - display: table; - width: 100%; - height: 100%; - background: #1c1c1c; - table-layout: fixed; -} - .aside { - display: table-cell; - height: 100%; - width: 250px; - vertical-align: top; - border-right: 1px solid #2f2f2f; - background: #1c1c1c; - } - .aside .nav-form input { - width: 180px; - } - .aside.aside_flux { - padding: 10px 0 40px; - } - .aside.aside_feed .nav-form input { - width: 140px; - } - .aside.aside_feed .nav-form .dropdown .dropdown-menu { - right: -20px; - } - .aside.aside_feed .nav-form .dropdown .dropdown-menu:after { - right: 33px; - } - - .nav-login { - display: none; - } - - .nav_menu { - width: 100%; - background: #1c1c1c; - border-bottom: 1px solid #2f2f2f; - text-align: center; - padding: 5px 0; - } - .nav_menu .search { - display:none; - } - -.favicon { - height: 16px; - width: 16px; -} - -.categories { - margin: 0; - padding: 0; - text-align: center; - list-style: none; -} - .category { - display: block; - width: 220px; - margin: 10px auto; - text-align: left; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .category .btn:first-child { - width: 195px; - position: relative; - } - .category.stick .btn:first-child { - width:160px; - } - .category .btn:first-child:not([data-unread="0"]):after { - content: attr(data-unread); - position: absolute; - top: 3px; right: 3px; - padding: 1px 5px; - background: #1a1a1a; - color: #888; - font-size: 90%; - border: 1px solid #000; - border-radius: 5px; - } - .category + .feeds:not(.active) { - display:none; - } - .categories .feeds { - width: 100%; - margin: 0; - list-style: none; - } - .categories .feeds .item.active { - background: #26303F; - } - .categories .feeds .item.active .feed { - color: #888; - } - .categories .feeds .item.empty .feed { - color: #e67e22; - } - .categories .feeds .item.empty.active { - background: #e67e22; - } - .categories .feeds .item.empty.active .feed { - color: #fff; - } - .categories .feeds .item.error .feed { - color: #BD362F; - } - .categories .feeds .item .feed { - display: inline-block; - margin: 0; - width: 165px; - line-height: 35px; - font-size: 90%; - vertical-align: middle; - text-align: left; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .feed:not([data-unread="0"]):before { - content: "(" attr(data-unread) ") "; - } - .categories .feeds .dropdown-menu { - left: 0; - } - .categories .feeds .dropdown-menu:after { - left: 2px; - } - .categories .feeds .item .dropdown-toggle > .icon { - visibility: hidden; - cursor: pointer; - } - .categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, - .categories .feeds .item:hover .dropdown-toggle > .icon, - .categories .feeds .item.active .dropdown-toggle > .icon { - background-color: #1c1c1c; - border-radius: 3px; - visibility: visible; - } - -.post { - padding: 10px 50px; -} - .post form { - margin: 10px 0; - } - -.day { - min-height: 50px; - padding: 0 10px; - font-size: 130%; - font-weight: bold; - line-height: 50px; - background: #1c1c1c; - border-top: 1px solid #888; - border-bottom: 1px solid #888; -} - .day:first-child { - border-top: none; - } - .day .name { - position: absolute; - right: 0; - width: 50%; - height: 1.5em; - padding: 0 10px 0 0; - overflow: hidden; - color: #aab; - font-size: 1.8em; - opacity: .3; - text-shadow: 0px -1px 0px #333; - font-style: italic; - white-space: nowrap; - text-overflow: ellipsis; - text-align: right; - } - -.flux { - border-left: 3px solid #aaa; - background: #1c1c1c; -} - .flux.not_read { - border-left: 3px solid #FF5300; - background: #1c1c1c; - } - .flux.favorite { - border-left: 3px solid #FFC300; - background: #1c1c1c; - } - .flux.current { - border-left: 3px solid #0062BE; - background: #1a1a1a; - } - - .flux_header { - background: inherit; - height: 25px; - font-size: 12px; - border-top: 1px solid #2f2f2f; - cursor: pointer; - } - .flux .item { - line-height: 40px; - white-space: nowrap; - } - .flux_header > .item { - overflow: hidden; - text-overflow: ellipsis; - } - .flux .item.manage { - width: 40px; - text-align: center; - } - .flux .item.website { - width: 200px; - } - .website .favicon { - padding: 5px; - } - .flux .item.title { - background: inherit; - } - .flux:hover .item.title { - border-right: 2px solid rgba(127, 127, 127, 0.1); - padding-right: 1em; - position: absolute; - } - .flux .item.title a { - color: #888; - outline: none; - } - .flux.not_read .item.title, - .flux.current .item.title { - font-weight: bold; - } - .flux .item.date { - width: 200px; - padding:0 5px 0 0; - text-align: right; - font-size: 10px; - color: #666; - } - .link { - width: 40px; - text-align: center; - } - -#stream.reader .flux { - padding: 0 0 30px; - border: none; - background: #1c1c1c; - color: #888; -} - #stream.reader .flux .author { - margin: 0 0 10px; - font-size: 90%; - color: #666; - } - -#stream.global { - text-align: center; -} - #stream.global .box-category { - display: inline-block; - width: 280px; - margin: 20px 10px; - vertical-align: top; - background: #1a1a1a; - border: 1px solid #000; - border-radius: 5px; - text-align: left; - box-shadow: 0 0 5px #2f2f2f; - } - #stream.global .category { - width: 100%; - margin: 0; - } - #stream.global .btn { - display: block; - width: auto; - height: 35px; - margin: 0; - padding: 0 10px; - background: #26303F; - border: none; - border-bottom: 1px solid #2f2f2f; - border-radius: 5px 5px 0 0; - line-height: 35px; - font-size: 120%; - } - #stream.global .btn:not([data-unread="0"]) { - font-weight:bold; - } - #stream.global .btn:first-child:not([data-unread="0"]):after { - top: 0; right: 5px; - border: 0; - background: none; - color: #666; - font-weight: bold; - box-shadow: none; - } - #stream.global .box-category .feeds { - display: block; - max-height: 250px; - margin: 0; - list-style: none; - overflow: auto; - } - #stream.global .box-category .feeds .item { - padding: 2px 10px; - font-size: 90%; - } - #stream.global .box-category .feed { - width: 220px; - } - -.content { - min-height: 150px; - max-width: 550px; - margin: 0 auto; - padding: 20px 10px; - line-height: 170%; - word-wrap: break-word; -} - .content h1, .content h2, .content h3 { - margin: 20px 0 5px; - } - .content > .title { - font-size: x-large; - margin: 0; - } - - .content p { - margin: 0 0 20px; - } - img.big { - display: block; - margin: 10px auto; - } - figure img.big { - margin: 0; - } - .content hr { - margin: 30px 0; - height: 1px; - background: #ddd; - border: 0; - } - .content pre { - margin: 10px auto; - padding: 10px; - overflow: auto; - background: #000; - color: #fff; - font-size: 110%; - } - .content q, .content blockquote { - display: block; - margin: 5px 0; - padding: 5px 20px; - font-style: italic; - border-left: 4px solid #ccc; - color: #666; - } - .content blockquote p { - margin: 0; - } - -#panel { - display: none; - position: fixed; - top: 10px; bottom: 10px; - left: 100px; right: 100px; - overflow: auto; - background: #1c1c1c; - border: 1px solid #95a5a6; - border-radius: 5px; -} - #panel .close { - position: fixed; - top: 10px; right: 0; - display: inline-block; - width: 26px; - height: 26px; - margin: 0 10px 0 0; - border: 1px solid #ccc; - border-radius: 20px; - text-align: center; - line-height: 26px; - background: #fff; - } - -#overlay { - display: none; - position: fixed; - top: 0; bottom: 0; - left: 0; right: 0; - background: rgba(0, 0, 0, 0.9); -} - -.flux_content .bottom { - font-size: 90%; - text-align: center; -} - -.hide_posts > :not(.active) > .flux_content { - display:none; -} - -/*** PAGINATION ***/ -.pagination { - display: table; - width: 100%; - margin: 0; - background: #1a1a1a; - text-align: center; - color: #888; - font-size: 80%; - line-height: 200%; - table-layout: fixed; -} - .pagination .item { - display: table-cell; - line-height: 40px; - } - .pagination .item.pager-current { - font-weight: bold; - font-size: 140%; - } - .pagination .pager-first, - .pagination .pager-previous, - .pagination .pager-next, - .pagination .pager-last { - width: 100px; - } - .pagination .item a { - display: block; - color: #333; - font-style: italic; - } - .pagination:first-child .item { - border-bottom: 1px solid #aaa; - } - .pagination:last-child .item { - border-top: 1px solid #aaa; - } - -#nav_entries { - display: table; - width: 250px; - height: 40px; - position: fixed; - bottom: 0; - left: 0; - margin: 0; - background: #1c1c1c; - border-top: 1px solid #2f2f2f; - text-align: center; - line-height: 40px; - table-layout: fixed; -} - #nav_entries .item { - display: table-cell; - width: 30%; - } - #nav_entries a { - display: block; - } - #nav_entries .i_up { - margin: 5px 0 0; - vertical-align: top; - } - -.loading { - background: url("loader.gif") center center no-repeat; - font-size: 0; -} - -#bigMarkAsRead { - display: block; - font-style: normal; - padding: 32px 0 64px 0; - text-align: center; - text-decoration: none; -} - #bigMarkAsRead:hover { - background: #1c1c1c; - color: #888; - } - .bigTick { - font-size: 72pt; - line-height: 1.6em; - } - -/*** NOTIFICATION ***/ -.notification { - position: absolute; - top: 10px; - left: 25%; right: 25%; - min-height: 30px; - padding: 10px; - line-height: 30px; - text-align: center; - border-radius: 5px; - box-shadow: 0 0 5px #666; - background: #1a1a1a; - color: #888; - font-weight: bold; - z-index: 10; -} - .notification.good { - border:1px solid #f4f899; - } - .notification.bad { - border:1px solid #f4a899; - } - .notification a.close { - display: inline-block; - width: 16px; - height: 16px; - float: right; - margin: -20px -20px 0 0; - padding: 5px; - background: #1a1a1a; - border-radius: 50px; - line-height: 16px; - } - .notification.good a.close{ - border:1px solid #f4f899; - } - .notification.bad a.close{ - border:1px solid #f4a899; - } - -.toggle_aside, .btn.toggle_aside { - display: none; -} - -.actualizeProgress { - position: fixed; - top: 10px; - left: 25%; right: 25%; - padding: 5px; - background: #1a1a1a; - text-align: center; - border: 1px solid #2f2f2f; - border-radius: 5px; -} - .actualizeProgress progress { - max-width: 100%; - vertical-align: middle; - } - .actualizeProgress .progress { - color: #888; - font-size: 90%; - vertical-align: middle; - } - -.logs { - border: 1px solid #aaa; -} - .log { - padding: 5px 2%; - overflow: auto; - background: #fafafa; - border-bottom: 1px solid #999; - color: #333; - font-size: 90%; - } - .log .date { - display: block; - } - .log.error { - background: #fdd; - color: #844; - } - .log.warning { - background: #ffe; - color: #c95; - } - .log.notice { - background: #f4f4f4; - color: #aaa; - } - .log.debug { - background: #111; - color: #eee; - } - -.form-group table { - border-collapse:collapse; - margin:10px 0 0 220px; - text-align:center; -} - -.form-group tr, .form-group th, .form-group td { - border:1px solid #2f2f2f; - font-weight:normal; - padding:.5em; -} - -select.number option { - text-align:right; -} - -@media(max-width: 840px) { - .header, - .aside .btn-important, - .aside .feeds .dropdown, - .flux_header .item.website span, - .item.date { - display: none; - } - .flux_header .item.website { - width: 40px; - text-align: center; - } - .flux_header .item.website .favicon { - padding: 12px; - } - - .nav-login { - display: block; - } - - .content { - font-size: 120%; - padding: 0; - } - - .pagination { - margin: 0 0 40px; - } - .pagination .pager-previous, .pagination .pager-next { - width: 100px; - } - - .toggle_aside, .btn.toggle_aside { - display: inline-block; - } - .aside { - position: fixed; - top: 0; left: 0; - width: 0; - overflow: hidden; - border-right: none; - z-index: 10; - transition: width 200ms linear; - } - .aside.aside_flux { - padding: 10px 0 0; - } - .aside:target { - width: 80%; - border-right: 1px solid #aaa; - overflow: auto; - } - .aside .toggle_aside { - position: absolute; - right: 0; - display: inline-block; - width: 26px; - height: 26px; - margin: 0 10px 0 0; - border: 1px solid #ccc; - border-radius: 20px; - text-align: center; - line-height: 26px; - } - .aside .categories { - margin: 30px 0; - } - - #nav_entries { - width: 100%; - } - - .nav_menu .btn { - margin: 5px 10px; - } - .nav_menu .stick { - margin: 0 10px; - } - .nav_menu .stick .btn { - margin: 5px 0; - } - .nav_menu .search { - display: inline-block; - max-width: 97%; - } - .nav_menu .search input { - max-width: 97%; - width: 90px; - } - .nav_menu .search input:focus { - width: 400px; - } - - #panel { - left: 5px; right: 5px; - } - - .day .date { - display: none; - } - .day .name { - height: 2.6em; - font-size: 1em; - text-shadow: none; - } - - .notification, - .actualizeProgress { - left: 10px; - right: 10px; - } -} - -/*** FALLBACK ***/ -.btn { - background: #1c1c1c; -} - .btn:hover { - background: -moz-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - background: -webkit-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - background: -o-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - background: -ms-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - } - .btn-important { - background: #26303F; - } - .btn-important:hover { - background: -moz-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - background: -webkit-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - background: -o-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - background: -ms-linear-gradient(top, #4A5D7A 0%, #26303F 100%); - } - .btn-attention { - background: #880011; - } - .btn-attention:hover { - background: -moz-linear-gradient(top, #cc0044 0%, #880011 100%); - background: -webkit-linear-gradient(top, #cc0044 0%, #880011 100%); - background: -o-linear-gradient(top, #cc0044 0%, #880011 100%); - background: -ms-linear-gradient(top, #cc0044 0%, #880011 100%); - } - -.dropdown-menu:after { - -moz-transform: rotate(45deg); - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); -} - -.nav-head { - background: #fff; - background: -moz-linear-gradient(top, #fff 0%, #f0f0f0 100%); - background: -webkit-linear-gradient(top, #fff 0%, #f0f0f0 100%); - background: -o-linear-gradient(top, #fff 0%, #f0f0f0 100%); - background: -ms-linear-gradient(top, #fff 0%, #f0f0f0 100%); -} - -.header > .item.search input { - -moz-transition: width 200ms linear; - -webkit-transition: width 200ms linear; - -o-transition: width 200ms linear; - -ms-transition: width 200ms linear; -} - -@media(max-width: 840px) { - .aside { - -moz-transition: width 200ms linear; - -webkit-transition: width 200ms linear; - -o-transition: width 200ms linear; - -ms-transition: width 200ms linear; - } -} - -@media print { - .header, - .aside, - .nav_menu, - .day, - .flux_header, - .flux_content .bottom, - .pagination { - display: none; - } - - html, body { - background: #fff; - color: #000; - font-family: Serif; - font-size: 12pt; - } - - #global, - .flux_content { - display: block !important; - } - - .flux_content .content { - width: 100% !important; - text-align: justify; - } - - .flux_content .content a { - color: #000; - } - .flux_content .content a:after { - content: " (" attr(href) ") "; - text-decoration: underline; - } -} diff --git a/p/themes/default_dark/global.css b/p/themes/default_dark/global.css deleted file mode 100644 index 04eb723f1..000000000 --- a/p/themes/default_dark/global.css +++ /dev/null @@ -1,487 +0,0 @@ -@charset "UTF-8"; - -/* FONTS */ -@font-face { - font-family: "OpenSans"; - src: url("../fonts/openSans.woff") format("woff"); -} - - -* { - margin: 0; - padding: 0; -} -html, body { - height: 100%; - font-size: 95%; - font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif; - color: #888; -} - -/* LIENS */ -a { - color: #6986B2; - text-decoration: none; -} - a:hover { - text-decoration: underline; - } - -/* LISTES */ -ul, ol, dl { - margin: 10px 0 10px 30px; - line-height: 190%; -} - dd { - margin: 0 0 10px 30px; - } - -/* TITRES */ -h1, h2, h3 { - min-height: 40px; - margin: 15px 0 5px; - line-height: 40px; -} - -/* IMG */ -figure { - margin: 5px 0 10px; - text-align: center; -} - figcaption { - display: inline-block; - padding: 3px 20px; - color: #999; - font-style: italic; - border-bottom: 1px solid #ccc; - } -img { - height: auto; - max-width: 100%; - vertical-align: middle; -} - a img { - border: none; - } - -/* VIDEOS */ -iframe, embed, object, video { - max-width: 100%; -} - -/* FORMULAIRES */ -legend { - display: block; - width: 100%; - margin: 20px 0 5px; - padding: 5px 0; - border-bottom: 1px solid #2f2f2f; - font-size: 150%; - clear: both; -} -label { - display: block; - min-height: 25px; - padding: 5px 0; - font-size: 14px; - line-height: 25px; - cursor: pointer; -} -input, select, textarea { - display: inline-block; - max-width: 100%; - min-height: 25px; - padding: 5px; - background: #333; - border: 1px solid #000; - border-radius: 3px; - color: #999; - line-height: 25px; - vertical-align: middle; - box-shadow: 0 2px 2px #1d1d1d inset; -} - option { - padding:0 .5em 0 .5em; - } - input[type="radio"], - input[type="checkbox"] { - width: 15px !important; - min-height: 15px !important; - } - input:focus, select:focus, textarea:focus { - color: #6986b2; - border-color: #2f2f2f; - } - input:invalid, select:invalid { - border-color: red; - box-shadow: 0 0 2px 1px red; - } - -.form-group { - margin: 0; -} - .form-group:after { - content: ""; - display: block; - clear: both; - } - .form-group.form-actions { - min-width: 250px; - padding: 5px 0; - background: #1a1a1a; - border-top: 1px solid #2f2f2f; - } - .form-group.form-actions .btn { - margin: 0 10px; - } - .form-group .group-name { - display: block; - float: left; - width: 200px; - padding: 10px 0; - text-align: right; - } - .form-group .group-controls { - min-width: 250px; - min-height: 25px; - margin: 0 0 0 220px; - padding: 5px 0; - } - .form-group .group-controls .control { - display: block; - min-height: 30px; - padding: 5px 0; - line-height: 25px; - font-size: 14px; - } - -.stick { - display: inline-block; - white-space: nowrap; - font-size: 0px; - vertical-align: middle; -} - .stick input, - .stick .btn { - border-radius: 0; - font-size: 14px; - } - .stick .btn:first-child, - .stick input:first-child { - border-radius: 3px 0 0 3px; - } - .stick .btn-important:first-child { - border-right: 1px solid #000; - } - .stick .btn:last-child, - .stick input:last-child { - border-radius: 0 3px 3px 0; - } - .stick .btn + .btn, - .stick .btn + input, - .stick input + .btn, - .stick input + input { - border-left: none; - } - .stick .btn + .dropdown > .btn { - border-left: none; - border-radius: 0 3px 3px 0; - } - .stick .btn + .dropdown a { - font-size: 12px; - } - -.btn { - display: inline-block; - min-height: 37px; - min-width: 15px; - padding: 5px 10px; - background: linear-gradient(to bottom, #fff 0%, #eee 100%); - border-radius: 3px; - border: 1px solid #000; - color: #888; - line-height: 20px; - vertical-align: middle; - cursor: pointer; - overflow: hidden; -} - a.btn { - min-height: 25px; - line-height: 25px; - } - .btn:hover { - background: linear-gradient(to bottom, #4A5D7A, #26303F); - text-decoration: none; - } - .btn.active, - .btn:active, - .dropdown-target:target ~ .btn.dropdown-toggle { - background: #26303F; - } - - .btn-important { - background: linear-gradient(to bottom, #0084CC, #0045CC); - color: #888888; - border: 1px solid #000000; - } - .btn-important:hover { - background: linear-gradient(to bottom, #0066CC, #0045CC); - } - .btn-important:active { - background: #0044CB; - box-shadow: none; - } - - .btn-attention { - background: linear-gradient(to bottom, #E95B57, #BD362F); - color: #888888; - border: 1px solid #000000; - } - .btn-attention:hover { - background: linear-gradient(to bottom, #D14641, #BD362F); - } - .btn-attention:active { - background: #BD362F; - box-shadow: none; - } - -/* NAVIGATION */ -.nav-list .nav-header, -.nav-list .item { - display: block; - height: 35px; - line-height: 35px; -} - .nav-list .item:hover { - background: #1a1a1a; - } - .nav-list .item:hover a { - color: #26303F; - } - .nav-list .item.active { - background: #26303F; - color: #1a1a1a; - } - .nav-list .item.active a { - color: #888; - } - .nav-list .disable { - color: #aaa; - background: #fafafa; - text-align: center; - } - .nav-list .item > * { - display: block; - padding: 0 10px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .nav-list a:hover { - text-decoration: none; - } - .nav-list .item.error a { - color: #BD362F; - } - .nav-list .item.active.error a { - color: #fff; - background: #BD362F; - } - .nav-list .item.empty a { - color: #f39c12; - } - .nav-list .item.active.empty a { - color: #fff; - background: #f39c12; - } - - .nav-list .nav-header { - padding: 0 10px; - background: #1a1a1a; - border-bottom: 1px solid #2f2f2f; - font-weight: bold; - } - .nav-list .separator { - display: block; - height: 0; - margin: 5px 0; - border-bottom: 1px solid #ddd; - } - - .nav-list .nav-form { - padding: 3px; - text-align: center; - } - -.nav-head { - display: block; - margin: 0; - background: linear-gradient(to bottom, #fff, #f0f0f0); - border-bottom: 1px solid #ddd; - text-align: right; -} - .nav-head .item { - display: inline-block; - padding: 5px 10px; - } - -/* HORIZONTAL-LIST */ -.horizontal-list { - display: table; - table-layout: fixed; - margin: 0; - padding: 0; - width: 100%; -} - .horizontal-list .item { - display: table-cell; - vertical-align: middle; - } - -/* DROPDOWN */ -.dropdown { - position: relative; - display: inline-block; -} - .dropdown-target { - display: none; - } - - .dropdown-menu { - display: none; - min-width: 200px; - margin: 5px 0 0; - padding: 5px 0; - position: absolute; - right: 0px; - background: #1a1a1a; - border: 1px solid #888; - border-radius: 5px; - text-align: left; - } - .dropdown-menu:after { - content: ""; - position: absolute; - top: -6px; - right: 13px; - width: 10px; - height: 10px; - background: #1a1a1a; - border-top: 1px solid #888; - border-left: 1px solid #888; - z-index: -10; - transform: rotate(45deg); - } - .dropdown-header { - display: block; - padding: 0 5px; - color: #888; - font-weight: bold; - font-size: 14px; - line-height: 30px; - } - .dropdown-menu .item { - display: block; - height: 30px; - font-size: 90%; - line-height: 30px; - } - .dropdown-menu > .item > a { - display: block; - padding: 0 25px; - line-height: 30px; - } - .dropdown-menu > .item:hover { - background: #26303F; - color: #888; - } - .dropdown-menu > .item[aria-checked="true"] > a:before { - content: '✓ '; - font-weight: bold; - margin: 0 0 0 -1.2em; - padding: 0 0.2em 0 0; - } - .dropdown-menu > .item:hover > a { - color: #888; - text-decoration: none; - } - .dropdown-menu .input { - display: block; - height: 40px; - font-size: 90%; - line-height: 30px; - } - .dropdown-menu .input select, - .dropdown-menu .input input { - display: block; - height: 20px; - width: 95%; - margin: auto; - padding: 2px 5px; - border-radius: 3px; - } - .dropdown-menu .input select { - width: 70%; - height: auto; - } - .dropdown-menu .separator { - display: block; - height: 0; - margin: 5px 0; - border-bottom: 1px solid #888; - } - .dropdown-target:target ~ .dropdown-menu { - display: block; - z-index: 10; - } - .dropdown-close { - display: inline; - } - .dropdown-close a { - font-size: 0; - position: fixed; - top: 0; bottom: 0; - left: 0; right: 0; - display: block; - z-index: -10; - } - -/* ALERTS */ -.alert { - display: block; - width: 90%; - margin: 15px auto; - padding: 10px 15px; - background: #1a1a1a; - border: 1px solid #ccc; - border-right: 1px solid #aaa; - border-bottom: 1px solid #aaa; - border-radius: 5px; - color: #aaa; -} - .alert-head { - margin: 0; - font-weight: bold; - font-size: 110%; - } - .alert-warn { - border: 1px solid #c95; - color: #c95; - } - .alert-success { - border: 1px solid #484; - color: #484; - } - .alert-error { - border: 1px solid #844; - color: #844; - } - -/* ICÔNES */ -.icon { - display: inline-block; - width: 16px; - height: 16px; - vertical-align: middle; - line-height: 16px; -} diff --git a/p/themes/default_dark/loader.gif b/p/themes/default_dark/loader.gif deleted file mode 100644 index 86022be71..000000000 Binary files a/p/themes/default_dark/loader.gif and /dev/null differ diff --git a/p/themes/default_dark/metadata.json b/p/themes/default_dark/metadata.json deleted file mode 100644 index 7504831a6..000000000 --- a/p/themes/default_dark/metadata.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Dark", - "author": "AD", - "description": "Le coté obscur du thème par défaut pour FreshRSS", - "version": 0.1, - "files": ["global.css", "freshrss.css"] -} diff --git a/p/themes/flat-design/freshrss.css b/p/themes/flat-design/freshrss.css deleted file mode 100644 index dca1b3f28..000000000 --- a/p/themes/flat-design/freshrss.css +++ /dev/null @@ -1,817 +0,0 @@ -@charset "UTF-8"; - -/* STRUCTURE */ -body { - background: #fafafa; -} - -.header { - display: table; - width: 100%; - table-layout: fixed; - background: #ecf0f1; -} - .header > .item { - display: table-cell; - padding: 10px 0; - vertical-align: middle; - text-align: center; - } - .header > .item.title { - width: 250px; - white-space: nowrap; - } - .logo { - display: inline-block; - font-size: 48px; - height: 32px; - width: 32px; - padding: 10px; - } - .header > .item.title h1 { - display: inline-block; - margin: 0; - } - .header > .item.search input { - width: 230px; - transition: width 200ms linear; - } - .header .item.search input:focus { - width: 330px; - } - .header > .item.configure { - width: 100px; - } - -.item a:hover { - text-decoration: none; -} - -#global { - display: table; - width: 100%; - height: 100%; - table-layout: fixed; -} - .aside { - display: table-cell; - height: 100%; - width: 250px; - vertical-align: top; - background: #ecf0f1; - } - .aside .nav-form input { - width: 180px; - } - .aside.aside_flux { - padding: 10px 0 40px; - } - .aside.aside_feed .nav-form input { - width: 140px; - } - .aside.aside_feed .nav-form .dropdown-menu { - right: -20px; - } - .aside.aside_feed .nav-form .dropdown-menu:after { - right: 33px; - } - - .nav-login { - display: none; - } - - .nav_menu { - width: 100%; - text-align: center; - padding: 5px 0; - } - .nav_menu .search { - display:none; - } - -.favicon { - height: 16px; - width: 16px; -} - -.categories { - margin: 0; - padding: 0; - text-align: center; - list-style: none; -} - .category { - display: block; - width: 220px; - margin: 10px auto; - text-align: left; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .category .btn:first-child { - width: 195px; - position: relative; - } - .category.stick .btn:first-child { - width:160px; - } - .category .btn:first-child:not([data-unread="0"]):after { - content: attr(data-unread); - position: absolute; - top: 5px; right: 5px; - padding: 0 5px; - color: #fff; - font-size: 90%; - background: #3498DB; - border-radius: 5px; - } - .category + .feeds:not(.active) { - display:none; - } - .categories .feeds { - width: 100%; - margin: 0 auto; - list-style: none; - } - .categories .feeds .item.active:after { - content: "⇢"; - line-height: 35px; - float: right; - } - .categories .feeds .item.empty .feed { - color: #e67e22; - } - .categories .feeds .item.error .feed { - color: #BD362F; - } - .categories .feeds .item .feed { - display: inline-block; - margin: 0; - width: 165px; - line-height: 35px; - font-size: 90%; - vertical-align: middle; - text-align: left; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .feed:not([data-unread="0"]) { - font-weight:bold; - } - .feed:not([data-unread="0"]):before { - content: "(" attr(data-unread) ") "; - } - .categories .feeds .dropdown-menu { - left: 0; - } - .categories .feeds .dropdown-menu:after { - left: 2px; - } - .categories .feeds .item .dropdown-toggle > .icon { - visibility: hidden; - cursor: pointer; - } - .categories .feeds .item .dropdown-target:target ~ .dropdown-toggle > .icon, - .categories .feeds .item:hover .dropdown-toggle > .icon, - .categories .feeds .item.active .dropdown-toggle > .icon { - background-color: #95a5a6; - border-radius: 3px; - visibility: visible; - } - .categories .btn:hover .notRead, - .categories .btn.active .notRead { - background: #2980B9; - border-left: 3px solid #3498DB; - } - -.post { - padding: 10px 50px; -} - .post form { - margin: 10px 0; - } - -.day { - padding: 5px 15px; - font-size: 130%; - font-weight: bold; - line-height: 50px; - border-left: 3px solid #ecf0f1; -} - .day .name { - position: absolute; - right: 0; - width: 50%; - height: 1.5em; - padding: 0 10px 0 0; - overflow: hidden; - color: #aab; - font-size: 1.8em; - opacity: .3; - font-style: italic; - text-align: right; - white-space: nowrap; - text-overflow: ellipsis; - z-index: -10; - } - -.flux { - border-left: 3px solid #ecf0f1; -} - .flux:hover { - background: #fff; - } - .flux.not_read { - border-left-color: #FF5300; - background: #FFF3ED; - } - .flux.favorite { - border-left-color: #FFC300; - background: #FFF6DA; - } - .flux.current { - border-left-color: #3498db; - background: #fff; - } - - .flux_header { - background: inherit; - height: 25px; - font-size: 12px; - border-top: 1px solid #ecf0f1; - cursor: pointer; - } - .flux .item { - line-height: 40px; - white-space: nowrap; - } - .flux_header > .item { - overflow: hidden; - text-overflow: ellipsis; - } - .flux .item.manage { - width: 40px; - text-align: center; - } - .flux .item.website { - width: 200px; - } - .website .favicon { - padding: 5px; - } - .flux .item.title { - background: inherit; - } - .flux:hover .item.title { - border-right: 2px solid rgba(127, 127, 127, 0.1); - padding-right: 1em; - position: absolute; - } - .flux .item.title a { - color: #333; - outline: none; - } - .flux.current .item.title, - .flux.not_read .item.title { - font-weight: bold; - } - .flux .item.date { - width: 200px; - padding:0 5px 0 0; - text-align: right; - font-size: 10px; - color: #666; - } - .link { - width: 40px; - text-align: center; - } - -#stream.reader .flux { - position: relative; - padding: 0 0 30px; - border: none; - background: #ecf0f1; - color: #34495e; - font-size: 120%; -} - #stream.reader .flux .author { - margin: 0 0 10px; - font-size: 90%; - color: #aaa; - } - -#stream.global { - text-align: center; -} - #stream.global .box-category { - display: inline-block; - width: 280px; - margin: 20px 10px; - vertical-align: top; - border: 1px solid #ddd; - border-radius: 5px; - text-align: left; - } - #stream.global .category { - width: 100%; - margin: 0; - } - #stream.global .btn { - display: block; - width: auto; - height: 35px; - margin: 0; - padding: 0 10px; - background: #ecf0f1; - color: #333; - border-bottom: 1px solid #ddd; - border-radius: 5px 5px 0 0; - line-height: 35px; - font-size: 120%; - } - #stream.global .btn:not([data-unread="0"]) { - font-weight:bold; - } - #stream.global .btn:first-child:not([data-unread="0"]):after { - top: 0; right: 5px; - border: 0; - background: none; - color: #333; - font-weight: bold; - box-shadow: none; - } - #stream.global .box-category .feeds { - display: block; - max-height: 250px; - margin: 0; - list-style: none; - overflow: auto; - } - #stream.global .box-category .feeds .item { - padding: 2px 10px; - font-size: 90%; - } - #stream.global .box-category .feed { - width: 220px; - } - -.content { - min-height: 150px; - max-width: 550px; - margin: 0 auto; - padding: 20px 10px; - line-height: 170%; - word-wrap: break-word; -} - .content h1, .content h2, .content h3 { - margin: 20px 0 5px; - } - .content > .title { - font-size: x-large; - margin: 0; - } - - .content p { - margin: 0 0 20px; - } - img.big { - display: block; - margin: 10px auto; - } - figure img.big { - margin: 0; - } - .content hr { - margin: 30px 0; - height: 1px; - background: #ddd; - border: 0; - } - .content pre { - margin: 10px auto; - padding: 10px; - overflow: auto; - background: #000; - color: #fff; - font-size: 110%; - } - .content q, .content blockquote { - display: block; - margin: 5px 0; - padding: 5px 20px; - font-style: italic; - border-left: 4px solid #ccc; - color: #666; - } - .content blockquote p { - margin: 0; - } - -#panel { - display: none; - position: fixed; - top: 10px; bottom: 10px; - left: 100px; right: 100px; - overflow: auto; - background: #fff; - border: 1px solid #95a5a6; - border-radius: 5px; -} - #panel .close { - position: fixed; - top: 10px; right: 0px; - display: inline-block; - width: 26px; - height: 26px; - margin: 0 10px 0 0; - border-radius: 3px; - text-align: center; - line-height: 24px; - background: #95a5a6; - } - #panel .close:hover { - background: #7f8c8d; - } - -#overlay { - display: none; - position: fixed; - top: 0; bottom: 0; - left: 0; right: 0; - background: rgba(0, 0, 0, 0.9); -} - -.flux_content .bottom { - font-size: 90%; - text-align: center; -} - -.hide_posts > :not(.active) > .flux_content { - display:none; -} - -/*** PAGINATION ***/ -.pagination { - display: table; - width: 100%; - margin: 0; - background: #ecf0f1; - text-align: center; - color: #000; - font-size: 80%; - line-height: 200%; - table-layout: fixed; - font-weight: bold; -} - .pagination .item { - display: table-cell; - line-height: 40px; - vertical-align: top; - } - .pagination .item.pager-current { - font-weight: bold; - font-size: 140%; - color: #ecf0f1; - background: #34495e; - } - .pagination .item.pager-first, - .pagination .item.pager-previous, - .pagination .item.pager-next, - .pagination .item.pager-last { - width: 100px; - } - .pagination .item a { - display: block; - color: #000; - font-weight: bold; - line-height: 40px; - } - .pagination .item a:hover { - color: #ecf0f1; - background: #34495e; - } - -#nav_entries { - display: table; - width: 250px; - height: 40px; - position: fixed; - bottom: 0; - left: 0; - margin: 0; - background: #34495e; - text-align: center; - line-height: 40px; - table-layout: fixed; -} - #nav_entries .item { - display: table-cell; - width: 30%; - } - #nav_entries a { - display: block; - } - #nav_entries .i_up { - margin: 5px 0 0; - vertical-align: top; - } - -.pagination .loading, -.pagination a:hover.loading { - background: url("loader.gif") center center no-repeat #34495e; - font-size: 0; -} - -#bigMarkAsRead { - background: #ecf0f1; - display: block; - font-style: normal; - padding: 32px 0 64px 0; - text-align: center; - text-decoration: none; - text-shadow: 0 -1px 0 #aaa; -} - #bigMarkAsRead:hover { - background: #34495e; - color: #fff; - } - .bigTick { - font-size: 72pt; - line-height: 1.6em; - } - -/*** NOTIFICATION ***/ -.notification { - position: absolute; - top: 10px; - left: 25%; right: 25%; - min-height: 30px; - padding: 10px; - line-height: 30px; - text-align: center; - border-radius: 3px; - background: #ddd; - color: #666; - font-weight: bold; - z-index: 10; -} - .notification.good { - background: #1abc9c; - color: #fff; - } - .notification.bad { - background: #e74c3c; - color: #fff; - } - .notification a.close { - display: inline-block; - width: 16px; - height: 16px; - float: right; - margin: -16px -16px 0 0; - padding: 5px; - border-radius: 3px; - line-height: 16px; - } - .notification.good a.close { - background: #1abc9c; - } - .notification.bad a.close { - background: #e74c3c; - } - -.toggle_aside, .btn.toggle_aside { - display: none; -} - -.actualizeProgress { - position: fixed; - top: 10px; - left: 25%; right: 25%; - padding: 5px; - background: #3498db; - color: #fff; - text-align: center; - border-radius: 3px; - font-weight: bold; -} - .actualizeProgress progress { - max-width: 100%; - width: 250px; - height: 15px; - vertical-align: middle; - background: #fff; - border: none; - } - .actualizeProgress .progress { - color: #ecf0f1; - } - -.logs { - border: 1px solid #34495e; -} - .log { - margin: 10px 0; - padding: 5px 2%; - overflow: auto; - background: #fafafa; - color: #666; - font-size: 90%; - } - .log>.date { - margin: 0 10px 0 0; - padding: 5px 10px; - border-radius: 20px; - } - .log.error>.date { - background: #e74c3c; - color: #fff; - } - .log.warning>.date { - background: #f39c12; - } - .log.notice>.date { - background: #ecf0f1; - } - .log.debug>.date { - background: #111; - color: #eee; - } - -.form-group table { - border-collapse:collapse; - margin:10px 0 0 220px; - text-align:center; -} - -.form-group tr, .form-group th, .form-group td { - font-weight:normal; - padding:.5em; -} - -select.number option { - text-align:right; -} - -@media(max-width: 840px) { - .header, - .aside .btn-important, - .aside .feeds .dropdown, - .flux_header .item.website span, - .item.date { - display: none; - } - .flux_header .item.website { - width: 40px; - text-align: center; - } - .flux_header .item.website .favicon { - padding: 12px; - } - - .nav-login { - display: block; - } - - .content { - font-size: 120%; - padding: 0; - } - - .pagination { - margin: 0 0 40px; - } - .pagination .pager-previous, .pagination .pager-next { - width: 100px; - } - - .toggle_aside, .btn.toggle_aside { - display: inline-block; - } - .aside { - position: fixed; - top: 0; left: 0; - width: 0; - overflow: hidden; - z-index: 10; - transition: width 200ms linear; - background: #ecf0f1; - } - .aside.aside_flux { - padding: 10px 0 0; - } - .aside:target { - width: 80%; - border-right: 1px solid #aaa; - overflow: auto; - } - .aside .toggle_aside { - position: absolute; - right: 10px; - display: inline-block; - width: 26px; - height: 26px; - margin: 0 10px 0 0; - border-radius: 3px; - text-align: center; - line-height: 24px; - background: #95a5a6; - } - .aside .toggle_aside:hover { - background: #7f8c8d; - } - .aside .categories { - margin: 30px 0; - } - - #nav_entries { - width: 100%; - } - - .nav_menu .btn { - margin: 5px 10px; - } - .nav_menu .stick { - margin: 0 10px; - } - .nav_menu .stick .btn { - margin: 5px 0; - } - .nav_menu .search { - display: inline-block; - max-width: 97%; - } - .nav_menu .search input { - max-width: 97%; - width: 90px; - } - .nav_menu .search input:focus { - width: 400px; - } - - #panel { - left: 5px; right: 5px; - } - - .day .date { - display: none; - } - .day .name { - height: 2.6em; - font-size: 1em; - text-shadow: none; - } - - .notification, - .actualizeProgress { - left: 10px; - right: 10px; - } -} - -/*** FALLBACK ***/ -.dropdown-menu:after { - -moz-transform: rotate(45deg); - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); -} - -@media print { - .header, - .aside, - .nav_menu, - .day, - .flux_header, - .flux_content .bottom, - .pagination { - display: none; - } - - html, body { - background: #fff; - color: #000; - font-family: Serif; - font-size: 12pt; - } - - #global, - .flux_content { - display: block !important; - } - - .flux_content .content { - width: 100% !important; - text-align: justify; - } - - .flux_content .content a { - color: #000; - } - .flux_content .content a:after { - content: " (" attr(href) ") "; - text-decoration: underline; - } -} diff --git a/p/themes/flat-design/global.css b/p/themes/flat-design/global.css deleted file mode 100644 index 5fd0a4dcf..000000000 --- a/p/themes/flat-design/global.css +++ /dev/null @@ -1,495 +0,0 @@ -@charset "UTF-8"; - -/* FONTS */ -@font-face { - font-family: "OpenSans"; - src: url("../fonts/openSans.woff") format("woff"); -} - - -* { - margin: 0; - padding: 0; -} -html, body { - height: 100%; - font-size: 95%; - font-family: "OpenSans", "Cantarell", "Helvetica", "Arial", sans-serif; -} - -/* LIENS */ -a { - color: #2980b9; - text-decoration: none; -} - a:hover { - text-decoration: underline; - } - -/* LISTES */ -ul, ol, dl { - margin: 10px 0 10px 30px; - line-height: 190%; -} - dd { - margin: 0 0 10px 30px; - } - -/* TITRES */ -h1, h2, h3 { - min-height: 40px; - margin: 15px 0 5px; - line-height: 40px; -} - -/* IMG */ -figure { - margin: 5px 0 10px; - text-align: center; -} - figcaption { - display: inline-block; - padding: 3px 20px; - color: #999; - font-style: italic; - border-bottom: 1px solid #ccc; - } -img { - height: auto; - max-width: 100%; - vertical-align: middle; -} - a img { - border: none; - } - -/* VIDEOS */ -iframe, embed, object, video { - max-width: 100%; -} - -/* FORMULAIRES */ -legend { - display: inline-block; - margin: 20px 0 5px; - padding: 5px 20px; - font-size: 150%; - clear: both; - background: #ecf0f1; - border-radius: 20px; -} -label { - display: block; - min-height: 25px; - padding: 5px 0; - font-size: 12px; - line-height: 25px; - cursor: pointer; - font-weight: bold; - color: #444; -} -input, select, textarea { - display: inline-block; - max-width: 100%; - min-height: 25px; - padding: 5px; - background: #FFF; - border: none; - border-bottom: 3px solid #ddd; - color: #666; - line-height: 25px; - vertical-align: middle; - border-radius: 5px; -} - option { - padding:0 .5em 0 .5em; - } - input[type="radio"], - input[type="checkbox"] { - width: 15px !important; - min-height: 15px !important; - } - input:focus, select:focus, textarea:focus { - color: #333; - border-color: #2980b9; - } - input:invalid, select:invalid { - border-color: red; - box-shadow: 0 0 2px 1px red; - } - -.form-group { - margin: 5px 0; - border: 1px solid transparent; -} - .form-group:after { - content: ""; - display: block; - clear: both; - } - .form-group:hover { - background: #fff; - border: 1px solid #eee; - border-radius: 3px; - } - .form-group.form-actions { - min-width: 250px; - margin: 20px 0; - padding: 5px 0; - background: #ecf0f1; - border-top: 3px solid #bdc3c7; - border-radius: 5px 5px 0 0; - } - .form-group.form-actions .btn { - margin: 0 10px; - } - .form-group .group-name { - display: block; - float: left; - width: 200px; - padding: 10px 0; - text-align: right; - } - .form-group .group-controls { - min-width: 250px; - min-height: 25px; - margin: 0 0 0 220px; - padding: 5px 0; - } - .form-group .group-controls label { - font-weight: normal; - font-size: 14px; - color: #000; - } - .form-group .group-controls .control { - display: block; - min-height: 30px; - padding: 5px 0; - line-height: 25px; - font-size: 14px; - } - -.stick { - display: inline-block; - white-space: nowrap; - font-size: 0px; - vertical-align: middle; -} - .stick .btn, - .stick input { - font-size: 14px; - border-radius: 0; - } - .stick .btn:first-child, - .stick input:first-child { - border-radius: 5px 0 0 5px; - } - .stick .btn:last-child, - .stick input:last-child, - .stick .btn + .dropdown > .btn { - border-radius: 0 5px 5px 0; - } - .stick .btn + .dropdown a { - font-size: 12px; - } - -.btn { - display: inline-block; - min-height: 38px; - min-width: 18px; - padding: 5px 10px; - background: #3498db; - border-radius: 5px; - border: none; - border-bottom: 3px solid #2980b9; - color: #fff; - line-height: 20px; - vertical-align: middle; - cursor: pointer; - overflow: hidden; -} - a.btn { - min-height: 25px; - line-height: 25px; - } - .btn.active, - .btn:active, - .btn:hover, - .dropdown-target:target ~ .btn.dropdown-toggle { - background: #2980b9; - text-decoration: none; - } - - .btn-important { - background: #e67e22; - color: #fff; - border-bottom: 3px solid #d35400; - } - .btn-important:active, - .btn-important:hover { - background: #d35400; - } - - .btn-attention { - background: #e74c3c; - color: #fff; - border-bottom: 3px solid #c0392b; - } - .btn-attention:hover, - .btn-attention:active { - background: #c0392b; - } - -/* NAVIGATION */ -.nav-list { - border-right: 1px solid #ecf0f1; -} -.nav-list .nav-header, -.nav-list .item { - display: block; - height: 35px; - line-height: 35px; - margin: 5px 0; -} - .nav-list .item:hover, - .nav-list .item.active { - background: #2980b9; - color: #fff; - } - .nav-list .item:hover a, - .nav-list .item.active a { - color: #fff; - } - .nav-list .disable { - color: #aaa; - background: #fafafa; - text-align: center; - } - .nav-list .item > * { - display: block; - padding: 0 10px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .nav-list a:hover { - text-decoration: none; - } - .nav-list .item.error a { - color: #BD362F; - } - .nav-list .item:hover.error a, - .nav-list .item.active.error a { - color: #fff; - background: #BD362F; - } - .nav-list .item.empty a { - color: #f39c12; - } - .nav-list .item:hover.empty a, - .nav-list .item.active.empty a { - color: #fff; - background: #f39c12; - } - - .nav-list .nav-header { - padding: 0 10px; - margin: 0; - color: #fff; - background: #34495e; - font-weight: bold; - } - .nav-list .separator { - display: block; - height: 0; - margin: 5px 0; - border-bottom: 1px solid #ddd; - } - - .nav-list .nav-form { - padding: 3px; - text-align: center; - } - -.nav-head { - display: block; - margin: 0; - background: #34495e; - color: #fff; - text-align: right; -} - .nav-head a { - color: #fff; - } - .nav-head .item { - display: inline-block; - padding: 5px 10px; - } - -/* HORIZONTAL-LIST */ -.horizontal-list { - display: table; - table-layout: fixed; - margin: 0; - padding: 0; - width: 100%; -} - .horizontal-list .item { - display: table-cell; - vertical-align: middle; - } - -/* DROPDOWN */ -.dropdown { - position: relative; - display: inline-block; -} - .dropdown-target { - display: none; - } - - .dropdown-menu { - display: none; - min-width: 200px; - margin: 5px 0 0; - padding: 5px 0; - position: absolute; - right: 0px; - background: #fff; - border: 1px solid #95a5a6; - border-radius: 3px; - text-align: left; - } - .dropdown-menu:after { - content: ""; - position: absolute; - top: -6px; - right: 13px; - width: 10px; - height: 10px; - background: #fff; - border-top: 1px solid #95a5a6; - border-left: 1px solid #95a5a6; - z-index: -10; - transform: rotate(45deg); - } - .dropdown-header { - display: block; - padding: 0 5px; - color: #34495e; - font-weight: bold; - font-size: 14px; - line-height: 30px; - } - .dropdown-menu > .item { - display: block; - height: 30px; - font-size: 90%; - line-height: 30px; - } - .dropdown-menu > .item > a { - display: block; - padding: 0 25px; - line-height: 30px; - } - .dropdown-menu > .item:hover > a { - background: #2980b9; - color: #fff; - } - .dropdown-menu > .item[aria-checked="true"] > a:before { - content: '✓ '; - font-weight: bold; - margin: 0 0 0 -1.2em; - padding: 0 0.2em 0 0; - } - .dropdown-menu > .item:hover > a { - color: #fff; - text-decoration: none; - } - .dropdown-menu .input { - display: block; - height: 40px; - font-size: 90%; - line-height: 30px; - } - .dropdown-menu label { - font-weight: normal; - } - .dropdown-menu .input select, - .dropdown-menu .input input { - display: block; - height: 20px; - width: 95%; - margin: auto; - padding: 2px 5px; - border-radius: 3px; - } - .dropdown-menu .input select { - width: 70%; - height: auto; - } - .dropdown-menu .separator { - display: block; - height: 0; - margin: 5px 0; - border-bottom: 1px solid #95a5a6; - } - .dropdown-target:target ~ .dropdown-menu { - display: block; - z-index: 10; - } - .dropdown-close { - display: inline; - } - .dropdown-close a { - font-size: 0; - position: fixed; - top: 0; bottom: 0; - left: 0; right: 0; - display: block; - z-index: -10; - } - -/* ALERTS */ -.alert { - display: block; - width: 90%; - margin: 15px auto; - padding: 10px 15px; - background: #f4f4f4; - border: 1px solid #ccc; - border-right: 1px solid #aaa; - border-bottom: 1px solid #aaa; - border-radius: 5px; - color: #aaa; - text-shadow: 0 0 1px #eee; -} - .alert-head { - margin: 0; - font-weight: bold; - font-size: 110%; - } - .alert-warn { - background: #ffe; - border: 1px solid #eeb; - color: #c95; - } - .alert-success { - background: #dfd; - border: 1px solid #cec; - color: #484; - } - .alert-error { - background: #fdd; - border: 1px solid #ecc; - color: #844; - } - -/* ICÔNES */ -.icon { - display: inline-block; - width: 16px; - height: 16px; - vertical-align: middle; - line-height: 16px; -} diff --git a/p/themes/flat-design/icons/add.svg b/p/themes/flat-design/icons/add.svg deleted file mode 100644 index 15767a3ad..000000000 --- a/p/themes/flat-design/icons/add.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/all.svg b/p/themes/flat-design/icons/all.svg deleted file mode 100644 index d20e0f5bf..000000000 --- a/p/themes/flat-design/icons/all.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/close.svg b/p/themes/flat-design/icons/close.svg deleted file mode 100644 index 629fda7ff..000000000 --- a/p/themes/flat-design/icons/close.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Gnome Symbolic Icon Theme - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/configure.svg b/p/themes/flat-design/icons/configure.svg deleted file mode 100644 index 969c5719f..000000000 --- a/p/themes/flat-design/icons/configure.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/down.svg b/p/themes/flat-design/icons/down.svg deleted file mode 100644 index 31730626f..000000000 --- a/p/themes/flat-design/icons/down.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/next.svg b/p/themes/flat-design/icons/next.svg deleted file mode 100644 index d75cc40f5..000000000 --- a/p/themes/flat-design/icons/next.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/prev.svg b/p/themes/flat-design/icons/prev.svg deleted file mode 100644 index 9ba03ceb2..000000000 --- a/p/themes/flat-design/icons/prev.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/refresh.svg b/p/themes/flat-design/icons/refresh.svg deleted file mode 100644 index 8f95bf443..000000000 --- a/p/themes/flat-design/icons/refresh.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/search.svg b/p/themes/flat-design/icons/search.svg deleted file mode 100644 index bca7571b4..000000000 --- a/p/themes/flat-design/icons/search.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/icons/up.svg b/p/themes/flat-design/icons/up.svg deleted file mode 100644 index 3ab11b168..000000000 --- a/p/themes/flat-design/icons/up.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - image/svg+xml - - Gnome Symbolic Icon Theme - - - - - - - Gnome Symbolic Icon Theme - - - - - - - - - - - - - - diff --git a/p/themes/flat-design/loader.gif b/p/themes/flat-design/loader.gif deleted file mode 100644 index ce36565b3..000000000 Binary files a/p/themes/flat-design/loader.gif and /dev/null differ diff --git a/p/themes/flat-design/metadata.json b/p/themes/flat-design/metadata.json deleted file mode 100644 index 6b94d11c2..000000000 --- a/p/themes/flat-design/metadata.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Flat design", - "author": "Marien Fressinaud", - "description": "Thème plat pour FreshRSS", - "version": 0.1, - "files": ["global.css", "freshrss.css"] -} \ No newline at end of file -- cgit v1.2.3 From 7261a551e908229ea4625b6645d490712570e71c Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 17 Jan 2014 22:47:21 +0100 Subject: Essaye d'éviter les problèmes d'auto-remplissage des champs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrige https://github.com/marienfressinaud/FreshRSS/issues/327#issuecomment-32635516 --- app/Controllers/feedController.php | 4 ++-- app/layout/aside_feed.phtml | 6 +++--- app/views/configure/feed.phtml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index 2d7c0ab43..d2117f665 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -30,8 +30,8 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $cat = $def_cat->id (); } - $user = Minz_Request::param ('username'); - $pass = Minz_Request::param ('password'); + $user = Minz_Request::param ('http_user'); + $pass = Minz_Request::param ('http_pass'); $params = array (); $transactionStarted = false; diff --git a/app/layout/aside_feed.phtml b/app/layout/aside_feed.phtml index 7fbccce1e..2446e72cb 100644 --- a/app/layout/aside_feed.phtml +++ b/app/layout/aside_feed.phtml @@ -1,7 +1,7 @@
    diff --git a/app/views/configure/feed.phtml b/app/views/configure/feed.phtml index a0fe39f8a..fc26ab58b 100644 --- a/app/views/configure/feed.phtml +++ b/app/views/configure/feed.phtml @@ -11,7 +11,7 @@

    - +
    -- cgit v1.2.3 From fb908d45577cd0f156104eb23b4b9859d40b6da2 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 00:34:46 +0100 Subject: Corrige rafraîchissement automatique après mise-à-jour avec Persona ou formulaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrige https://github.com/marienfressinaud/FreshRSS/issues/327#issuecomment-31958851 --- app/Controllers/feedController.php | 1 + app/views/helpers/javascript_vars.phtml | 3 --- p/i/install.php | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index d2117f665..c40b3c400 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -164,6 +164,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO = new FreshRSS_FeedDAO (); $entryDAO = new FreshRSS_EntryDAO (); + Minz_Session::_param('actualize_feeds', false); $id = Minz_Request::param ('id'); $force = Minz_Request::param ('force', false); diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml index 3fd5580f4..0ecdc1bca 100644 --- a/app/views/helpers/javascript_vars.phtml +++ b/app/views/helpers/javascript_vars.phtml @@ -42,6 +42,3 @@ echo 'str_confirmation="', Minz_Translate::t('confirm_action'), '"', ",\n"; $autoActualise = Minz_Session::param('actualize_feeds', false); echo 'auto_actualize_feeds=', $autoActualise ? 'true' : 'false', ";\n"; -if ($autoActualise) { - Minz_Session::_param('actualize_feeds', false); -} diff --git a/p/i/install.php b/p/i/install.php index 294231fc0..18dfc1449 100644 --- a/p/i/install.php +++ b/p/i/install.php @@ -817,7 +817,7 @@ function printStep2 () {
    - +
    -- cgit v1.2.3 From 41033768c3eacbd564c3ec15455587e4f725a055 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 01:00:17 +0100 Subject: Mode anonyme pour connexion avec formulaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/361 --- app/Controllers/usersController.php | 2 +- app/views/configure/users.phtml | 18 ++++-------------- lib/Minz/Configuration.php | 9 +++++---- 3 files changed, 10 insertions(+), 19 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/usersController.php b/app/Controllers/usersController.php index 7e44b3d35..a044cd25b 100644 --- a/app/Controllers/usersController.php +++ b/app/Controllers/usersController.php @@ -57,8 +57,8 @@ class FreshRSS_users_Controller extends Minz_ActionController { $auth_type = Minz_Request::param('auth_type', 'none'); if ($anon != Minz_Configuration::allowAnonymous() || $auth_type != Minz_Configuration::authType()) { - Minz_Configuration::_allowAnonymous($anon); Minz_Configuration::_authType($auth_type); + Minz_Configuration::_allowAnonymous($anon); $ok &= Minz_Configuration::writeFile(); } } diff --git a/app/views/configure/users.phtml b/app/views/configure/users.phtml index 602dfaf62..3f352f9bf 100644 --- a/app/views/configure/users.phtml +++ b/app/views/configure/users.phtml @@ -58,20 +58,11 @@
    -
    -
    - - -
    -
    - - - - Mozilla Persona
    @@ -81,7 +72,8 @@ conf->token; ?>
    - + />
    @@ -92,8 +84,6 @@ - -
    diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 433992e0d..72e2cedc0 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -113,7 +113,7 @@ class Minz_Configuration { } public static function _allowAnonymous($allow = false) { - self::$allow_anonymous = (bool)$allow; + self::$allow_anonymous = ((bool)$allow) && self::canLogIn(); } public static function _authType($value) { $value = strtolower($value); @@ -125,6 +125,7 @@ class Minz_Configuration { self::$auth_type = $value; break; } + self::_allowAnonymous(self::$allow_anonymous); } /** @@ -252,12 +253,12 @@ class Minz_Configuration { if (isset ($general['default_user'])) { self::$default_user = $general['default_user']; } - if (isset ($general['allow_anonymous'])) { - self::$allow_anonymous = ((bool)($general['allow_anonymous'])) && ($general['allow_anonymous'] !== 'no'); - } if (isset ($general['auth_type'])) { self::_authType($general['auth_type']); } + if (isset ($general['allow_anonymous'])) { + self::$allow_anonymous = ((bool)($general['allow_anonymous'])) && ($general['allow_anonymous'] !== 'no'); + } // Base de données if (isset ($ini_array['db'])) { -- cgit v1.2.3 From dda7b002def51405bab82bd4a31fc3ad8c33c572 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 01:38:11 +0100 Subject: Corrections mode anonyme avec formulaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/361 --- app/Controllers/indexController.php | 4 ++-- app/layout/header.phtml | 17 ++++++++++----- app/views/helpers/view/login.phtml | 43 ------------------------------------- app/views/index/formLogin.phtml | 43 +++++++++++++++++++++++++++++++++++++ app/views/index/index.phtml | 4 ++-- 5 files changed, 59 insertions(+), 52 deletions(-) delete mode 100644 app/views/helpers/view/login.phtml create mode 100644 app/views/index/formLogin.phtml (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 772a08f30..d05a106bb 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -296,7 +296,6 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function formLoginAction () { - $this->view->_useLayout (false); if (Minz_Request::isPost()) { $ok = false; $nonce = Minz_Session::param('nonce'); @@ -327,9 +326,10 @@ class FreshRSS_index_Controller extends Minz_ActionController { ); Minz_Session::_param('notification', $notif); } + $this->view->_useLayout(false); + Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } invalidateHttpCache(); - Minz_Request::forward(array('c' => 'index', 'a' => 'index'), true); } public function formLogoutAction () { diff --git a/app/layout/header.phtml b/app/layout/header.phtml index e90da6196..d43f682b0 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -90,9 +90,16 @@ if (Minz_Configuration::canLogIn()) { - -
    - -
    - +
    diff --git a/app/views/helpers/view/login.phtml b/app/views/helpers/view/login.phtml deleted file mode 100644 index e4a24f9ed..000000000 --- a/app/views/helpers/view/login.phtml +++ /dev/null @@ -1,43 +0,0 @@ -
    - -

    -
    - -
    - -
    -
    -
    - -
    - - - -
    -
    -
    -
    - -
    -
    -

    FreshRSS

    -

    - -

    -
    diff --git a/app/views/index/formLogin.phtml b/app/views/index/formLogin.phtml new file mode 100644 index 000000000..e4a24f9ed --- /dev/null +++ b/app/views/index/formLogin.phtml @@ -0,0 +1,43 @@ +
    + +

    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +

    FreshRSS

    +

    + +

    +
    diff --git a/app/views/index/index.phtml b/app/views/index/index.phtml index 9b69233e9..9a7c9f3b9 100644 --- a/app/views/index/index.phtml +++ b/app/views/index/index.phtml @@ -21,8 +21,8 @@ if ($this->loginOk || Minz_Configuration::allowAnonymous()) { if ($token_is_ok) { $this->renderHelper ('view/rss_view'); } else { - $this->renderHelper ('view/login'); + Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'), true); } } else { - $this->renderHelper ('view/login'); + Minz_Request::forward(array('c' => 'index', 'a' => 'formLogin'), true); } -- cgit v1.2.3 From 69f7bce75b6f45b7f8be376a5d9c14d5da62c91d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 16:41:10 +0100 Subject: Changements de vues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correction d'un bug JavaScript récent dans la vue globale. Corrections de nombreux bugs lors des changements de vue https://github.com/marienfressinaud/FreshRSS/issues/346 et amélioration des performances pour la génération des URL en évitant beaucoup d'appels de fonctions https://github.com/marienfressinaud/FreshRSS/pull/362 De plus, dans les URL, is_favorite et is_read ont maintenant une valeur par défaut de 1, ce qui évite de les écrire dans beaucoup de cas. Suppression des espaces blancs de la sortie HTML au niveau de quelques boucles critiques. --- app/Controllers/entryController.php | 13 ++-- app/i18n/en.php | 2 +- app/i18n/fr.php | 2 +- app/layout/aside_flux.phtml | 73 +++++++++--------- app/layout/nav_menu.phtml | 8 +- app/views/entry/bookmark.phtml | 2 +- app/views/entry/read.phtml | 2 +- app/views/helpers/view/normal_view.phtml | 122 +++++++++++++++++-------------- p/scripts/global_view.js | 7 +- 9 files changed, 131 insertions(+), 100 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index a24dfe6d6..ded7598d9 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -10,6 +10,11 @@ class FreshRSS_entry_Controller extends Minz_ActionController { } $this->params = array (); + $output = Minz_Request::param('output', ''); + if (($output != '') && ($this->conf->view_mode !== $output)) { + $this->params['output'] = $output; + } + $this->redirect = false; $ajax = Minz_Request::param ('ajax'); if ($ajax) { @@ -34,13 +39,10 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $this->redirect = true; $id = Minz_Request::param ('id'); - $is_read = Minz_Request::param ('is_read'); $get = Minz_Request::param ('get'); $nextGet = Minz_Request::param ('nextGet', $get); $idMax = Minz_Request::param ('idMax', 0); - $is_read = (bool)$is_read; - $entryDAO = new FreshRSS_EntryDAO (); if ($id == false) { if (!$get) { @@ -63,7 +65,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { break; } if ($nextGet !== 'a') { - $this->params = array ('get' => $nextGet); + $this->params['get'] = $nextGet; } } @@ -73,6 +75,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { ); Minz_Session::_param ('notification', $notif); } else { + $is_read = (bool)(Minz_Request::param ('is_read', true)); $entryDAO->markRead ($id, $is_read); } } @@ -83,7 +86,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $id = Minz_Request::param ('id'); if ($id) { $entryDAO = new FreshRSS_EntryDAO (); - $entryDAO->markFavorite ($id, Minz_Request::param ('is_favorite')); + $entryDAO->markFavorite ($id, (bool)(Minz_Request::param ('is_favorite', true))); } } diff --git a/app/i18n/en.php b/app/i18n/en.php index 1b76dfc52..01d86e5da 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -161,7 +161,7 @@ return array ( 'current_user' => 'Current user', 'default_user' => 'Username of the default user (maximum 16 alphanumeric characters)', - 'password_form' =>'Password
    (for the Web-form login method)', + 'password_form' => 'Password
    (for the Web-form login method)', 'persona_connection_email' => 'Login mail address
    (for Mozilla Persona)', 'allow_anonymous' => 'Allow anonymous reading of the articles of the default user (%s)', 'auth_token' => 'Authentication token', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 5d54dc2dd..2c44aa8d0 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -160,7 +160,7 @@ return array ( 'think_to_add' => 'Pensez à en ajouter !', 'current_user' => 'Utilisateur actuel', - 'password_form' =>'Mot de passe
    (pour connexion par formulaire)', + 'password_form' => 'Mot de passe
    (pour connexion par formulaire)', 'default_user' => 'Nom de l’utilisateur par défaut (16 caractères alphanumériques maximum)', 'persona_connection_email' => 'Adresse courriel de connexion
    (pour Mozilla Persona)', 'allow_anonymous' => 'Autoriser la lecture anonyme des articles de l’utilisateur par défaut (%s)', diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index f2f85df30..f7d8b12b9 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -13,15 +13,15 @@
  • - 'index', 'a' => 'index', 'params' => array()); if ($this->conf->view_mode !== Minz_Request::param('output', 'normal')) { - $output = array('output', 'normal'); + $arUrl['params']['output'] = 'normal'; } ?>
  • - + @@ -30,43 +30,46 @@
  • - cat_aside as $cat) { ?> - feeds (); ?> - -
  • - get_c == $cat->id ()) { $c_active = true; } ?> -
    - name (); ?> - -
    - -
      - id (); $nbEntries = $feed->nbEntries (); - $f_active = ($this->get_f == $feed_id); - ?> -
    • - - ✇ - name(); ?> -
    • - -
    -
  • - + cat_aside as $cat) { + $feeds = $cat->feeds (); + if (!empty ($feeds)) { + ?>
  • get_c == $cat->id ()) { + $c_active = true; + } + ?>
      id (); + $nbEntries = $feed->nbEntries (); + $f_active = ($this->get_f == $feed_id); + ?>
    • ✇ name(); ?>
  • - @@ -79,7 +82,7 @@
  • -
  • +
  • diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index 705ed3314..b9ce33295 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -56,7 +56,13 @@ } $p = isset($this->entries[0]) ? $this->entries[0] : null; $idMax = $p === null ? '0' : $p->id(); - $markReadUrl = _url ('entry', 'read', 'is_read', 1, 'get', $get, 'nextGet', $nextGet, 'idMax', $idMax); + + $arUrl = array('c' => 'entry', 'a' => 'read', 'params' => array('get' => $get, 'nextGet' => $nextGet, 'idMax' => $idMax)); + $output = Minz_Request::param('output', ''); + if (($output != '') && ($this->conf->view_mode !== $output)) { + $arUrl['params']['output'] = $output; + } + $markReadUrl = Minz_Url::display($arUrl); Minz_Session::_param ('markReadUrl', $markReadUrl); ?> diff --git a/app/views/entry/bookmark.phtml b/app/views/entry/bookmark.phtml index 55b2d3d3d..c1fc32b7f 100755 --- a/app/views/entry/bookmark.phtml +++ b/app/views/entry/bookmark.phtml @@ -1,7 +1,7 @@ entries)) { $bottomline_link = $this->conf->bottomline_link; ?> -
    - entries as $item) { ?> +
    entries as $item) { + if ($display_today && $item->isDay (FreshRSS_Days::TODAY, $this->today)) { + ?>
    currentName; ?>
    isDay (FreshRSS_Days::YESTERDAY, $this->today)) { + ?>
    currentName; ?>
    isDay (FreshRSS_Days::BEFORE_YESTERDAY, $this->today)) { + ?>
    currentName; ?>
    isDay (FreshRSS_Days::TODAY, $this->today)) { ?> -
    - - - currentName; ?> -
    - - isDay (FreshRSS_Days::YESTERDAY, $this->today)) { ?> -
    - - - currentName; ?> -
    - - isDay (FreshRSS_Days::BEFORE_YESTERDAY, $this->today)) { ?> -
    - - currentName; ?> -
    - - -
    diff --git a/p/scripts/global_view.js b/p/scripts/global_view.js index 0cdcdd3fa..c34fbf1a7 100644 --- a/p/scripts/global_view.js +++ b/p/scripts/global_view.js @@ -50,11 +50,14 @@ function init_global_view() { $(".nav_menu #nav_menu_read_all, .nav_menu .toggle_aside").remove(); - init_stream_delegates($("#panel")); + init_stream($("#panel")); } function init_all_global_view() { - if (!(window.$ && window.init_stream_delegates)) { + if (!(window.$ && window.init_stream)) { + if (window.console) { + console.log('FreshRSS Global view waiting for JS…'); + } window.setTimeout(init_all_global_view, 50); //Wait for all js to be loaded return; } -- cgit v1.2.3 From fdd179d344dbe8734480b83bb7a0fba189275d5a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 18 Jan 2014 19:29:44 +0100 Subject: Corrections vue globale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contribue à https://github.com/marienfressinaud/FreshRSS/issues/353 --- app/Controllers/entryController.php | 2 +- app/Controllers/indexController.php | 6 ++---- app/views/helpers/view/global_view.phtml | 16 ++++++++++------ lib/Minz/View.php | 6 +++--- p/scripts/global_view.js | 10 ++++++++++ p/scripts/main.js | 8 +++++--- 6 files changed, 31 insertions(+), 17 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index ded7598d9..1756c91e5 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -11,7 +11,7 @@ class FreshRSS_entry_Controller extends Minz_ActionController { $this->params = array (); $output = Minz_Request::param('output', ''); - if (($output != '') && ($this->conf->view_mode !== $output)) { + if (($output != '') && ($this->view->conf->view_mode !== $output)) { $this->params['output'] = $output; } diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index d05a106bb..45ded6fd4 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -46,10 +46,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { // no layout for RSS output $this->view->_useLayout (false); header('Content-Type: application/rss+xml; charset=utf-8'); - } else { - if ($output === 'global') { - Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); - } + } elseif ($output === 'global') { + Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); } $this->view->cat_aside = $this->catDAO->listCategories (); diff --git a/app/views/helpers/view/global_view.phtml b/app/views/helpers/view/global_view.phtml index ca7b66e0c..4fb807649 100644 --- a/app/views/helpers/view/global_view.phtml +++ b/app/views/helpers/view/global_view.phtml @@ -2,18 +2,22 @@
    'index', 'a' => 'index', 'params' => array()); + if ($this->conf->view_mode !== 'normal') { + $arUrl['params']['output'] = 'normal'; } + $p = Minz_Request::param('state', ''); + if (($p != '') && ($this->conf->default_view !== $p)) { + $arUrl['params']['state'] = $p; + } + foreach ($this->cat_aside as $cat) { $feeds = $cat->feeds (); if (!empty ($feeds)) { ?>
    @@ -22,7 +26,7 @@ nbNotRead (); ?>
  • ✇ - + name(); ?>
  • diff --git a/lib/Minz/View.php b/lib/Minz/View.php index ba9555cd7..e170bd406 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -63,7 +63,7 @@ class Minz_View { * Affiche la Vue en elle-même */ public function render () { - if ((@include($this->view_filename)) === false) { + if ((include($this->view_filename)) === false) { Minz_Log::record ('File not found: `' . $this->view_filename . '`', Minz_Log::NOTICE); @@ -79,7 +79,7 @@ class Minz_View { . self::LAYOUT_PATH_NAME . '/' . $part . '.phtml'; - if ((@include($fic_partial)) === false) { + if ((include($fic_partial)) === false) { Minz_Log::record ('File not found: `' . $fic_partial . '`', Minz_Log::WARNING); @@ -95,7 +95,7 @@ class Minz_View { . '/views/helpers/' . $helper . '.phtml'; - if ((@include($fic_helper)) === false) {; + if ((include($fic_helper)) === false) {; Minz_Log::record ('File not found: `' . $fic_helper . '`', Minz_Log::WARNING); diff --git a/p/scripts/global_view.js b/p/scripts/global_view.js index c34fbf1a7..3973cb2ec 100644 --- a/p/scripts/global_view.js +++ b/p/scripts/global_view.js @@ -24,6 +24,16 @@ function load_panel(link) { // en en ouvrant une autre ensuite, on se retrouve au même point de scroll $("#panel").scrollTop(0); + $('#nav_menu_read_all a, #bigMarkAsRead').click(function () { + $.ajax({ + url: $(this).attr("href"), + async: false + }); + //$("#panel .close").first().click(); + window.location.reload(false); + return false; + }); + panel_loading = false; }); } diff --git a/p/scripts/main.js b/p/scripts/main.js index 5bdc316db..3f6352baf 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -137,7 +137,9 @@ function mark_favorite(active) { if (active.closest('div').hasClass('not_read')) { var elem = $('#aside_flux .favorites').children(':first').get(0), feed_unreads = elem ? (parseInt(elem.getAttribute('data-unread'), 10) || 0) : 0; - elem.setAttribute('data-unread', Math.max(0, feed_unreads + inc)); + if (elem) { + elem.setAttribute('data-unread', Math.max(0, feed_unreads + inc)); + } } }); } @@ -559,6 +561,8 @@ function load_more_posts() { } function init_load_more(box) { + box_load_more = box; + var $next_link = $("#load_more"); if (!$next_link.length) { // no more article to load @@ -566,8 +570,6 @@ function init_load_more(box) { return; } - box_load_more = box; - url_load_more = $next_link.attr("href"); var $prefetch = $('#prefetch'); if ($prefetch.attr('href') !== url_load_more) { -- cgit v1.2.3 From e98ac32716dfe9343baa5bce7ad2c1e98c40540c Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Wed, 22 Jan 2014 19:06:46 -0500 Subject: Ajout de statistiques de l'application Conflicts: app/i18n/en.php app/i18n/fr.php --- app/Controllers/indexController.php | 10 ++ app/Models/StatsDAO.php | 178 ++++++++++++++++++++++++++++++++++++ app/i18n/en.php | 14 ++- app/i18n/fr.php | 14 ++- app/layout/aside_flux.phtml | 2 +- app/layout/header.phtml | 1 + app/views/index/stats.phtml | 92 +++++++++++++++++++ p/scripts/flotr2.min.js | 27 ++++++ p/themes/Origine/freshrss.css | 43 +++++++++ 9 files changed, 378 insertions(+), 3 deletions(-) create mode 100644 app/Models/StatsDAO.php create mode 100644 app/views/index/stats.phtml create mode 100644 p/scripts/flotr2.min.js (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 45ded6fd4..84ed992f9 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -6,12 +6,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { private $entryDAO; private $feedDAO; private $catDAO; + private $statsDAO; function __construct($router) { parent::__construct($router); $this->entryDAO = new FreshRSS_EntryDAO (); $this->feedDAO = new FreshRSS_FeedDAO (); $this->catDAO = new FreshRSS_CategoryDAO (); + $this->statsDAO = new FreshRSS_StatsDAO (); } public function indexAction () { @@ -198,6 +200,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { return false; } } + + public function statsAction () { + Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); + $this->view->repartition = $this->statsDAO->calculateEntryRepartition(); + $this->view->count = ($this->statsDAO->calculateEntryCount()); + $this->view->feedByCategory = $this->statsDAO->calculateFeedByCategory(); + $this->view->entryByCategory = $this->statsDAO->calculateEntryByCategory(); + } public function aboutAction () { Minz_View::prependTitle (Minz_Translate::t ('about') . ' · '); diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php new file mode 100644 index 000000000..a317c8f27 --- /dev/null +++ b/app/Models/StatsDAO.php @@ -0,0 +1,178 @@ +prefix}entry AS e +, {$this->prefix}feed AS f +WHERE e.id_feed = f.id +AND f.priority = 10 +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $repartition['main_stream'] = $res[0]; + + // Generates the repartition for all entries + $sql = <<prefix}entry AS e +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + $repartition['all_feeds'] = $res[0]; + + return $repartition; + } + + /** + * Calculates entry count per day on a 30 days period. + * Returns the result as a JSON string. + * + * @return string + */ + public function calculateEntryCount() { + $count = array(); + + // Generates a list of 30 last day to be sure we always have 30 days. + // If we do not do that kind of thing, we'll end up with holes in the + // days if the user do not have a lot of feeds. + $sql = <<bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + foreach ($res as $value) { + $count[$value['day']] = 0; + } + + // Get stats per day for the last 30 days and applies the result on + // the array created with the last query. + $sql = <<prefix}entry AS e +WHERE FROM_UNIXTIME(e.date, '%Y%m%d') BETWEEN DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -30 DAY), '%Y%m%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY), '%Y%m%d') +GROUP BY day +ORDER BY day ASC +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + foreach ($res as $value) { + $count[$value['day']] = (int) $value['count']; + } + + return $this->convertToSerie($count); + } + + /** + * Calculates feed count per category. + * Returns the result as a JSON string. + * + * @return string + */ + public function calculateFeedByCategory() { + $sql = <<prefix}category AS c, +{$this->prefix}feed AS f +WHERE c.id = f.category +GROUP BY label +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + return $this->convertToPieSerie($res); + } + + /** + * Calculates entry count per category. + * Returns the result as a JSON string. + * + * @return string + */ + public function calculateEntryByCategory() { + $sql = <<prefix}category AS c, +{$this->prefix}feed AS f, +{$this->prefix}entry AS e +WHERE c.id = f.category +AND f.id = e.id_feed +GROUP BY label +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + $res = $stm->fetchAll(PDO::FETCH_ASSOC); + + return $this->convertToPieSerie($res); + } + + private function convertToSerie($data) { + $serie = array(); + + foreach ($data as $key => $value) { + $serie[] = array($key, $value); + } + + return json_encode($serie); + } + + private function convertToPieSerie($data) { + $serie = array(); + + foreach ($data as $value) { + $value['data'] = array(array(0, (int)$value['data'])); + $serie[] = $value; + } + + return json_encode($serie); + } + +} diff --git a/app/i18n/en.php b/app/i18n/en.php index 5d9b01f0b..963105f35 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -13,6 +13,7 @@ return array ( 'category' => 'Category', 'shortcuts' => 'Shortcuts', 'about' => 'About', + 'stats' => 'Statistics', 'your_rss_feeds' => 'Your RSS feeds', 'add_rss_feed' => 'Add a RSS feed', @@ -20,7 +21,8 @@ return array ( 'import_export_opml' => 'Import / export (OPML)', 'subscription_management' => 'Subscriptions management', - 'all_feeds' => 'Main stream', + 'main_stream' => 'Main stream', + 'all_feeds' => 'All feeds', 'favorite_feeds' => 'Favourites (%d)', 'not_read' => '%d unread', 'not_reads' => '%d unread', @@ -297,4 +299,14 @@ return array ( // format for date() function, %s allows to indicate month in letter 'format_date' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y', 'format_date_hour' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y \a\t H\:i', + + 'status_favorites' => 'favourites', + 'status_read' => 'read', + 'status_unread' => 'unread', + 'status_total' => 'total', + + 'stats_entry_repartition' => 'Entry repartition', + 'stats_entry_per_day' => 'Entry per day (last 30 days)', + 'stats_feed_per_category' => 'Feed per category', + 'stats_entry_per_category' => 'Entry per category', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index fd2257c70..c8b03ef8b 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -13,6 +13,7 @@ return array ( 'category' => 'Catégorie', 'shortcuts' => 'Raccourcis', 'about' => 'À propos', + 'stats' => 'Statistiques', 'your_rss_feeds' => 'Vos flux RSS', 'add_rss_feed' => 'Ajouter un flux RSS', @@ -20,7 +21,8 @@ return array ( 'import_export_opml' => 'Importer / exporter (OPML)', 'subscription_management' => 'Gestion des abonnements', - 'all_feeds' => 'Flux principal', + 'main_stream' => 'Flux principal', + 'all_feeds' => 'Tous les flux', 'favorite_feeds' => 'Favoris (%d)', 'not_read' => '%d non lu', 'not_reads' => '%d non lus', @@ -297,4 +299,14 @@ return array ( // format pour la fonction date(), %s permet d'indiquer le mois en toutes lettres 'format_date' => 'j %s Y', 'format_date_hour' => 'j %s Y \à H\:i', + + 'status_favorites' => 'favoris', + 'status_read' => 'lus', + 'status_unread' => 'non lus', + 'status_total' => 'total', + + 'stats_entry_repartition' => 'Répartition des articles', + 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', + 'stats_feed_per_category' => 'Flux par categorie', + 'stats_entry_per_category' => 'Article par categorie', ); diff --git a/app/layout/aside_flux.phtml b/app/layout/aside_flux.phtml index f7d8b12b9..1e3211886 100644 --- a/app/layout/aside_flux.phtml +++ b/app/layout/aside_flux.phtml @@ -23,7 +23,7 @@ diff --git a/app/layout/header.phtml b/app/layout/header.phtml index b00c30e71..eef53a3fd 100644 --- a/app/layout/header.phtml +++ b/app/layout/header.phtml @@ -74,6 +74,7 @@ if (Minz_Configuration::canLogIn()) {
  • +
  • + + +

    + +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    repartition['main_stream']['total']?>repartition['all_feeds']['total']?>
    repartition['main_stream']['read']?>repartition['all_feeds']['read']?>
    repartition['main_stream']['unread']?>repartition['all_feeds']['unread']?>
    repartition['main_stream']['favorite']?>repartition['all_feeds']['favorite']?>
    +
    + +
    +

    +
    +
    + +
    +

    +
    +
    +
    + +
    +

    +
    +
    +
    + +
    + + \ No newline at end of file diff --git a/p/scripts/flotr2.min.js b/p/scripts/flotr2.min.js new file mode 100644 index 000000000..c3601e827 --- /dev/null +++ b/p/scripts/flotr2.min.js @@ -0,0 +1,27 @@ +/*! + * bean.js - copyright Jacob Thornton 2011 + * https://github.com/fat/bean + * MIT License + * special thanks to: + * dean edwards: http://dean.edwards.name/ + * dperini: https://github.com/dperini/nwevents + * the entire mootools team: github.com/mootools/mootools-core + *//*global module:true, define:true*/ +!function(e,t,n){typeof module!="undefined"?module.exports=n(e,t):typeof define=="function"&&typeof define.amd=="object"?define(n):t[e]=n(e,t)}("bean",this,function(e,t){var n=window,r=t[e],i=/over|out/,s=/[^\.]*(?=\..*)\.|.*/,o=/\..*/,u="addEventListener",a="attachEvent",f="removeEventListener",l="detachEvent",c=document||{},h=c.documentElement||{},p=h[u],d=p?u:a,v=Array.prototype.slice,m=/click|mouse|menu|drag|drop/i,g=/^touch|^gesture/i,y={one:1},b=function(e,t,n){for(n=0;n0){t=t.split(" ");for(f=t.length;f--;)_(e,t[f],n);return e}u=c&&t.replace(o,""),u&&w[u]&&(u=w[u].type);if(!t||c){if(a=c&&t.replace(s,""))a=a.split(".");l(e,u,n,a)}else if(typeof t=="function")l(e,null,t);else for(r in t)t.hasOwnProperty(r)&&_(e,r,t[r]);return e},D=function(e,t,n,r,i){var s,o,u,a,f=n,l=n&&typeof n=="string";if(t&&!n&&typeof t=="object")for(s in t)t.hasOwnProperty(s)&&D.apply(this,[e,s,t[s]]);else{a=arguments.length>3?v.call(arguments,3):[],o=(l?n:t).split(" "),l&&(n=M(t,f=r,i))&&(a=v.call(a,1)),this===y&&(n=L(_,e,t,n,f));for(u=o.length;u--;)O(e,o[u],n,f,a)}return e},P=function(){return D.apply(y,arguments)},H=p?function(e,t,r){var i=c.createEvent(e?"HTMLEvents":"UIEvents");i[e?"initEvent":"initUIEvent"](t,!0,!0,n,1),r.dispatchEvent(i)}:function(e,t,n){n=S(n,e),e?n.fireEvent("on"+t,c.createEventObject()):n["_on"+t]++},B=function(e,t,n){var r,i,u,a,f,l=t.split(" ");for(r=l.length;r--;){t=l[r].replace(o,"");if(a=l[r].replace(s,""))a=a.split(".");if(!a&&!n&&e[d])H(b[t],t,e);else{f=T.get(e,t),n=[!1].concat(n);for(i=0,u=f.length;i=r.computed&&(r={value:e,computed:o})}),r.value},S.min=function(e,t,n){if(!t&&S.isArray(e))return Math.min.apply(Math,e);var r={computed:Infinity};return x(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;or?1:0}),"value")},S.groupBy=function(e,t){var n={};return x(e,function(e,r){var i=t(e,r);(n[i]||(n[i]=[])).push(e)}),n},S.sortedIndex=function(e,t,n){n||(n=S.identity);var r=0,i=e.length;while(r>1;n(e[s])=0})})},S.difference=function(e,t){return S.filter(e,function(e){return!S.include(t,e)})},S.zip=function(){var e=o.call(arguments),t=S.max(S.pluck(e,"length")),n=new Array(t);for(var r=0;r=0;n--)t=[e[n].apply(this,t)];return t[0]}},S.after=function(e,t){return function(){if(--e<1)return t.apply(this,arguments)}},S.keys=w||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)f.call(e,n)&&(t[t.length]=n);return t},S.values=function(e){return S.map(e,S.identity)},S.functions=S.methods=function(e){var t=[];for(var n in e)S.isFunction(e[n])&&t.push(n);return t.sort()},S.extend=function(e){return x(o.call(arguments,1),function(t){for(var n in t)t[n]!==void 0&&(e[n]=t[n])}),e},S.defaults=function(e){return x(o.call(arguments,1),function(t){for(var n in t)e[n]==null&&(e[n]=t[n])}),e},S.clone=function(e){return S.isArray(e)?e.slice():S.extend({},e)},S.tap=function(e,t){return t(e),e},S.isEqual=function(e,t){if(e===t)return!0;var n=typeof e,r=typeof t;if(n!=r)return!1;if(e==t)return!0;if(!e&&t||e&&!t)return!1;e._chain&&(e=e._wrapped),t._chain&&(t=t._wrapped);if(e.isEqual)return e.isEqual(t);if(t.isEqual)return t.isEqual(e);if(S.isDate(e)&&S.isDate(t))return e.getTime()===t.getTime();if(S.isNaN(e)&&S.isNaN(t))return!1;if(S.isRegExp(e)&&S.isRegExp(t))return e.source===t.source&&e.global===t.global&&e.ignoreCase===t.ignoreCase&&e.multiline===t.multiline;if(n!=="object")return!1;if(e.length&&e.length!==t.length)return!1;var i=S.keys(e),s=S.keys(t);if(i.length!=s.length)return!1;for(var o in e)if(!(o in t)||!S.isEqual(e[o],t[o]))return!1;return!0},S.isEmpty=function(e){if(S.isArray(e)||S.isString(e))return e.length===0;for(var t in e)if(f.call(e,t))return!1;return!0},S.isElement=function(e){return!!e&&e.nodeType==1},S.isArray=b||function(e){return a.call(e)==="[object Array]"},S.isObject=function(e){return e===Object(e)},S.isArguments=function(e){return!!e&&!!f.call(e,"callee")},S.isFunction=function(e){return!!(e&&e.constructor&&e.call&&e.apply)},S.isString=function(e){return!!(e===""||e&&e.charCodeAt&&e.substr)},S.isNumber=function(e){return!!(e===0||e&&e.toExponential&&e.toFixed)},S.isNaN=function(e){return e!==e},S.isBoolean=function(e){return e===!0||e===!1},S.isDate=function(e){return!!(e&&e.getTimezoneOffset&&e.setUTCFullYear)},S.isRegExp=function(e){return!(!(e&&e.test&&e.exec)||!e.ignoreCase&&e.ignoreCase!==!1)},S.isNull=function(e){return e===null},S.isUndefined=function(e){return e===void 0},S.noConflict=function(){return e._=t,this},S.identity=function(e){return e},S.times=function(e,t,n){for(var r=0;r/g,interpolate:/<%=([\s\S]+?)%>/g},S.template=function(e,t){var n=S.templateSettings,r="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(n.interpolate,function(e,t){return"',"+t.replace(/\\'/g,"'")+",'"}).replace(n.evaluate||null,function(e,t){return"');"+t.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",i=new Function("obj",r);return t?i(t):i};var k=function(e){this._wrapped=e};S.prototype=k.prototype;var L=function(e,t){return t?S(e).chain():e},A=function(e,t){k.prototype[e]=function(){var e=o.call(arguments);return u.call(e,this._wrapped),L(t.apply(S,e),this._chain)}};S.mixin(S),x(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];k.prototype[e]=function(){return t.apply(this._wrapped,arguments),L(this._wrapped,this._chain)}}),x(["concat","join","slice"],function(e){var t=r[e];k.prototype[e]=function(){return L(t.apply(this._wrapped,arguments),this._chain)}}),k.prototype.chain=function(){return this._chain=!0,this},k.prototype.value=function(){return this._wrapped}})(); +/** + * Flotr2 (c) 2012 Carl Sutherland + * MIT License + * Special thanks to: + * Flotr: http://code.google.com/p/flotr/ (fork) + * Flot: https://github.com/flot/flot (original fork) + */ +(function(){var e=this,t=this.Flotr,n;n={_:_,bean:bean,isIphone:/iphone/i.test(navigator.userAgent),isIE:navigator.appVersion.indexOf("MSIE")!=-1?parseFloat(navigator.appVersion.split("MSIE")[1]):!1,graphTypes:{},plugins:{},addType:function(e,t){n.graphTypes[e]=t,n.defaultOptions[e]=t.options||{},n.defaultOptions.defaultType=n.defaultOptions.defaultType||e},addPlugin:function(e,t){n.plugins[e]=t,n.defaultOptions[e]=t.options||{}},draw:function(e,t,r,i){return i=i||n.Graph,new i(e,t,r)},merge:function(e,t){var r,i,s=t||{};for(r in e)i=e[r],i&&typeof i=="object"?i.constructor===Array?s[r]=this._.clone(i):i.constructor!==RegExp&&!this._.isElement(i)&&!i.jquery?s[r]=n.merge(i,t?t[r]:undefined):s[r]=i:s[r]=i;return s},clone:function(e){return n.merge(e,{})},getTickSize:function(e,t,r,i){var s=(r-t)/e,o=n.getMagnitude(s),u=10,a=s/o;return a<1.5?u=1:a<2.25?u=2:a<3?u=i===0?2:2.5:a<7.5&&(u=5),u*o},defaultTickFormatter:function(e,t){return e+""},defaultTrackFormatter:function(e){return"("+e.x+", "+e.y+")"},engineeringNotation:function(e,t,n){var r=["Y","Z","E","P","T","G","M","k",""],i=["y","z","a","f","p","n","µ","m",""],s=r.length;n=n||1e3,t=Math.pow(10,t||2);if(e===0)return 0;if(e>1)while(s--&&e>=n)e/=n;else{r=i,s=r.length;while(s--&&e<1)e*=n}return Math.round(e*t)/t+r[s]},getMagnitude:function(e){return Math.pow(10,Math.floor(Math.log(e)/Math.LN10))},toPixel:function(e){return Math.floor(e)+.5},toRad:function(e){return-e*(Math.PI/180)},floorInBase:function(e,t){return t*Math.floor(e/t)},drawText:function(e,t,r,i,s){if(!e.fillText){e.drawText(t,r,i,s);return}s=this._.extend({size:n.defaultOptions.fontSize,color:"#000000",textAlign:"left",textBaseline:"bottom",weight:1,angle:0},s),e.save(),e.translate(r,i),e.rotate(s.angle),e.fillStyle=s.color,e.font=(s.weight>1?"bold ":"")+s.size*1.3+"px sans-serif",e.textAlign=s.textAlign,e.textBaseline=s.textBaseline,e.fillText(t,0,0),e.restore()},getBestTextAlign:function(e,t){return t=t||{textAlign:"center",textBaseline:"middle"},e+=n.getTextAngleFromAlign(t),Math.abs(Math.cos(e))>.01&&(t.textAlign=Math.cos(e)>0?"right":"left"),Math.abs(Math.sin(e))>.01&&(t.textBaseline=Math.sin(e)>0?"top":"bottom"),t},alignTable:{"right middle":0,"right top":Math.PI/4,"center top":Math.PI/2,"left top":3*(Math.PI/4),"left middle":Math.PI,"left bottom":-3*(Math.PI/4),"center bottom":-Math.PI/2,"right bottom":-Math.PI/4,"center middle":0},getTextAngleFromAlign:function(e){return n.alignTable[e.textAlign+" "+e.textBaseline]||0},noConflict:function(){return e.Flotr=t,this}},e.Flotr=n})(),Flotr.defaultOptions={colors:["#00A8F0","#C0D800","#CB4B4B","#4DA74D","#9440ED"],ieBackgroundColor:"#FFFFFF",title:null,subtitle:null,shadowSize:4,defaultType:null,HtmlText:!0,fontColor:"#545454",fontSize:7.5,resolution:1,parseFloat:!0,preventDefault:!0,xaxis:{ticks:null,minorTicks:null,showLabels:!0,showMinorLabels:!1,labelsAngle:0,title:null,titleAngle:0,noTicks:5,minorTickFreq:null,tickFormatter:Flotr.defaultTickFormatter,tickDecimals:null,min:null,max:null,autoscale:!1,autoscaleMargin:0,color:null,mode:"normal",timeFormat:null,timeMode:"UTC",timeUnit:"millisecond",scaling:"linear",base:Math.E,titleAlign:"center",margin:!0},x2axis:{},yaxis:{ticks:null,minorTicks:null,showLabels:!0,showMinorLabels:!1,labelsAngle:0,title:null,titleAngle:90,noTicks:5,minorTickFreq:null,tickFormatter:Flotr.defaultTickFormatter,tickDecimals:null,min:null,max:null,autoscale:!1,autoscaleMargin:0,color:null,scaling:"linear",base:Math.E,titleAlign:"center",margin:!0},y2axis:{titleAngle:270},grid:{color:"#545454",backgroundColor:null,backgroundImage:null,watermarkAlpha:.4,tickColor:"#DDDDDD",labelMargin:3,verticalLines:!0,minorVerticalLines:null,horizontalLines:!0,minorHorizontalLines:null,outlineWidth:1,outline:"nsew",circular:!1},mouse:{track:!1,trackAll:!1,position:"se",relative:!1,trackFormatter:Flotr.defaultTrackFormatter,margin:5,lineColor:"#FF3F19",trackDecimals:1,sensibility:2,trackY:!0,radius:3,fillColor:null,fillOpacity:.4}},function(){function t(e,t,n,r){this.rgba=["r","g","b","a"];var i=4;while(-1<--i)this[this.rgba[i]]=arguments[i]||(i==3?1:0);this.normalize()}var e=Flotr._,n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]};t.prototype={scale:function(t,n,r,i){var s=4;while(-1<--s)e.isUndefined(arguments[s])||(this[this.rgba[s]]*=arguments[s]);return this.normalize()},alpha:function(t){return!e.isUndefined(t)&&!e.isNull(t)&&(this.a=t),this.normalize()},clone:function(){return new t(this.r,this.b,this.g,this.a)},limit:function(e,t,n){return Math.max(Math.min(e,n),t)},normalize:function(){var e=this.limit;return this.r=e(parseInt(this.r,10),0,255),this.g=e(parseInt(this.g,10),0,255),this.b=e(parseInt(this.b,10),0,255),this.a=e(this.a,0,1),this},distance:function(e){if(!e)return;e=new t.parse(e);var n=0,r=3;while(-1<--r)n+=Math.abs(this[this.rgba[r]]-e[this.rgba[r]]);return n},toString:function(){return this.a>=1?"rgb("+[this.r,this.g,this.b].join(",")+")":"rgba("+[this.r,this.g,this.b,this.a].join(",")+")"},contrast:function(){var e=1-(.299*this.r+.587*this.g+.114*this.b)/255;return e<.5?"#000000":"#ffffff"}},e.extend(t,{parse:function(e){if(e instanceof t)return e;var r;if(r=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(e))return new t(parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16));if(r=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(e))return new t(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10));if(r=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(e))return new t(parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16),parseInt(r[3]+r[3],16));if(r=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(e))return new t(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10),parseFloat(r[4]));if(r=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(e))return new t(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55);if(r=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(e))return new t(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55,parseFloat(r[4]));var i=(e+"").replace(/^\s*([\S\s]*?)\s*$/,"$1").toLowerCase();return i=="transparent"?new t(255,255,255,0):(r=n[i])?new t(r[0],r[1],r[2]):new t(0,0,0,0)},processColor:function(n,r){var i=r.opacity;if(!n)return"rgba(0, 0, 0, 0)";if(n instanceof t)return n.alpha(i).toString();if(e.isString(n))return t.parse(n).alpha(i).toString();var s=n.colors?n:{colors:n};if(!r.ctx)return e.isArray(s.colors)?t.parse(e.isArray(s.colors[0])?s.colors[0][1]:s.colors[0]).alpha(i).toString():"rgba(0, 0, 0, 0)";s=e.extend({start:"top",end:"bottom"},s),/top/i.test(s.start)&&(r.x1=0),/left/i.test(s.start)&&(r.y1=0),/bottom/i.test(s.end)&&(r.x2=0),/right/i.test(s.end)&&(r.y2=0);var o,u,a,f=r.ctx.createLinearGradient(r.x1,r.y1,r.x2,r.y2);for(o=0;o=h)break}h=i[v][0],p=i[v][1],p=="year"&&(h=Flotr.getTickSize(s.noTicks*r.year,a,f,0),h==.5&&(p="month",h=6)),e.tickUnit=p,e.tickSize=h;var g=h*r[p];m=new Date(a);switch(p){case"millisecond":y("Milliseconds");break;case"second":y("Seconds");break;case"minute":y("Minutes");break;case"hour":y("Hours");break;case"month":y("Month");break;case"year":y("FullYear")}g>=r.second&&t(m,"Milliseconds",o,0),g>=r.minute&&t(m,"Seconds",o,0),g>=r.hour&&t(m,"Minutes",o,0),g>=r.day&&t(m,"Hours",o,0),g>=r.day*4&&t(m,"Date",o,1),g>=r.year&&t(m,"Month",o,0);var b=0,w=NaN,E;do{E=w,w=m.getTime(),c.push({v:w/u,label:d(w/u,e)});if(p=="month")if(h<1){t(m,"Date",o,1);var S=m.getTime();t(m,"Month",o,n(m,"Month",o)+1);var x=m.getTime();m.setTime(w+b*r.hour+(x-S)*h),b=n(m,"Hours",o),t(m,"Hours",o,0)}else t(m,"Month",o,n(m,"Month",o)+h);else p=="year"?t(m,"FullYear",o,n(m,"FullYear",o)+h):m.setTime(w+g)}while(w0)return{x:t.touches[0].pageX,y:t.touches[0].pageY};if(!e._.isUndefined(t.changedTouches)&&t.changedTouches.length>0)return{x:t.changedTouches[0].pageX,y:t.changedTouches[0].pageY};if(t.pageX||t.pageY)return{x:t.pageX,y:t.pageY};if(t.clientX||t.clientY){var n=document,r=n.body,i=n.documentElement;return{x:t.clientX+r.scrollLeft+i.scrollLeft,y:t.clientY+r.scrollTop+i.scrollTop}}}}}(),function(){var e=Flotr,t=e.DOM,n=e._,r=function(e){this.o=e};r.prototype={dimensions:function(e,t,n,r){return e?this.o.html?this.html(e,this.o.element,n,r):this.canvas(e,t):{width:0,height:0}},canvas:function(t,n){if(!this.o.textEnabled)return;n=n||{};var r=this.measureText(t,n),i=r.width,s=n.size||e.defaultOptions.fontSize,o=n.angle||0,u=Math.cos(o),a=Math.sin(o),f=2,l=6,c;return c={width:Math.abs(u*i)+Math.abs(a*s)+f,height:Math.abs(a*i)+Math.abs(u*s)+l},c},html:function(e,n,r,i){var s=t.create("div");return t.setStyles(s,{position:"absolute",top:"-10000px"}),t.insert(s,'
    '+e+"
    "),t.insert(this.o.element,s),t.size(s)},measureText:function(t,r){var i=this.o.ctx,s;return!i.fillText||e.isIphone&&i.measure?{width:i.measure(t,r)}:(r=n.extend({size:e.defaultOptions.fontSize,weight:1,angle:0},r),i.save(),i.font=(r.weight>1?"bold ":"")+r.size*1.3+"px sans-serif",s=i.measureText(t),i.restore(),s)}},Flotr.Text=r}(),function(){function i(e,n,r){return t.observe.apply(this,arguments),this._handles.push(arguments),this}var e=Flotr.DOM,t=Flotr.EventAdapter,n=Flotr._,r=Flotr;Graph=function(e,i,s){this._setEl(e),this._initMembers(),this._initPlugins(),t.fire(this.el,"flotr:beforeinit",[this]),this.data=i,this.series=r.Series.getSeries(i),this._initOptions(s),this._initGraphTypes(),this._initCanvas(),this._text=new r.Text({element:this.el,ctx:this.ctx,html:this.options.HtmlText,textEnabled:this.textEnabled}),t.fire(this.el,"flotr:afterconstruct",[this]),this._initEvents(),this.findDataRanges(),this.calculateSpacing(),this.draw(n.bind(function(){t.fire(this.el,"flotr:afterinit",[this])},this))},Graph.prototype={destroy:function(){t.fire(this.el,"flotr:destroy"),n.each(this._handles,function(e){t.stopObserving.apply(this,e)}),this._handles=[],this.el.graph=null},observe:i,_observe:i,processColor:function(e,t){var i={x1:0,y1:0,x2:this.plotWidth,y2:this.plotHeight,opacity:1,ctx:this.ctx};return n.extend(i,t),r.Color.processColor(e,i)},findDataRanges:function(){var e=this.axes,t,i,s;n.each(this.series,function(e){s=e.getRange(),s&&(t=e.xaxis,i=e.yaxis,t.datamin=Math.min(s.xmin,t.datamin),t.datamax=Math.max(s.xmax,t.datamax),i.datamin=Math.min(s.ymin,i.datamin),i.datamax=Math.max(s.ymax,i.datamax),t.used=t.used||s.xused,i.used=i.used||s.yused)},this),!e.x.used&&!e.x2.used&&(e.x.used=!0),!e.y.used&&!e.y2.used&&(e.y.used=!0),n.each(e,function(e){e.calculateRange()});var o=n.keys(r.graphTypes),u=!1;n.each(this.series,function(e){if(e.hide)return;n.each(o,function(t){e[t]&&e[t].show&&(this.extendRange(t,e),u=!0)},this),u||this.extendRange(this.options.defaultType,e)},this)},extendRange:function(e,t){this[e].extendRange&&this[e].extendRange(t,t.data,t[e],this[e]),this[e].extendYRange&&this[e].extendYRange(t.yaxis,t.data,t[e],this[e]),this[e].extendXRange&&this[e].extendXRange(t.xaxis,t.data,t[e],this[e])},calculateSpacing:function(){var e=this.axes,t=this.options,r=this.series,i=t.grid.labelMargin,s=this._text,o=e.x,u=e.x2,a=e.y,f=e.y2,l=t.grid.outlineWidth,c,h,p,d;n.each(e,function(e){e.calculateTicks(),e.calculateTextDimensions(s,t)}),d=s.dimensions(t.title,{size:t.fontSize*1.5},"font-size:1em;font-weight:bold;","flotr-title"),this.titleHeight=d.height,d=s.dimensions(t.subtitle,{size:t.fontSize},"font-size:smaller;","flotr-subtitle"),this.subtitleHeight=d.height;for(h=0;h1&&(this.multitouches=n.touches),t.fire(e,"flotr:mousedown",[event,this]),this.observe(document,"touchend",r)},this)),this.observe(this.overlay,"touchmove",n.bind(function(n){var r=this.getEventPosition(n);this.options.preventDefault&&n.preventDefault(),i=!0,this.multitouches||n.touches&&n.touches.length>1?this.multitouches=n.touches:s||t.fire(e,"flotr:mousemove",[event,r,this]),this.lastMousePos=r},this))):this.observe(this.overlay,"mousedown",n.bind(this.mouseDownHandler,this)).observe(e,"mousemove",n.bind(this.mouseMoveHandler,this)).observe(this.overlay,"click",n.bind(this.clickHandler,this)).observe(e,"mouseout",function(n){t.fire(e,"flotr:mouseout",n)})},_initCanvas:function(){function l(i,s){return i||(i=e.create("canvas"),typeof FlashCanvas!="undefined"&&typeof i.getContext=="function"&&(FlashCanvas.initElement(i),this.isFlashCanvas=!0),i.className="flotr-"+s,i.style.cssText="position:absolute;left:0px;top:0px;",e.insert(t,i)),n.each(a,function(t,n){e.show(i);if(s=="canvas"&&i.getAttribute(n)===t)return;i.setAttribute(n,t*r.resolution),i.style[n]=t+"px"}),i.context_=null,i}function c(e){window.G_vmlCanvasManager&&window.G_vmlCanvasManager.initElement(e);var t=e.getContext("2d");return window.G_vmlCanvasManager||t.scale(r.resolution,r.resolution),t}var t=this.el,r=this.options,i=t.children,s=[],o,u,a,f;for(u=i.length;u--;)o=i[u],!this.canvas&&o.className==="flotr-canvas"?this.canvas=o:!this.overlay&&o.className==="flotr-overlay"?this.overlay=o:s.push(o);for(u=s.length;u--;)t.removeChild(s[u]);e.setStyles(t,{position:"relative"}),a={},a.width=t.clientWidth,a.height=t.clientHeight;if(a.width<=0||a.height<=0||r.resolution<=0)throw"Invalid dimensions for plot, width = "+a.width+", height = "+a.height+", resolution = "+r.resolution;this.canvas=l(this.canvas,"canvas"),this.overlay=l(this.overlay,"overlay"),this.ctx=c(this.canvas),this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.octx=c(this.overlay),this.octx.clearRect(0,0,this.overlay.width,this.overlay.height),this.canvasHeight=a.height,this.canvasWidth=a.width,this.textEnabled=!!this.ctx.drawText||!!this.ctx.fillText},_initPlugins:function(){n.each(r.plugins,function(e,t){n.each(e.callbacks,function(e,t){this.observe(this.el,t,n.bind(e,this))},this),this[t]=r.clone(e),n.each(this[t],function(e,r){n.isFunction(e)&&(this[t][r]=n.bind(e,this))},this)},this)},_initOptions:function(e){var i=r.clone(r.defaultOptions);i.x2axis=n.extend(n.clone(i.xaxis),i.x2axis),i.y2axis=n.extend(n.clone(i.yaxis),i.y2axis),this.options=r.merge(e||{},i),this.options.grid.minorVerticalLines===null&&this.options.xaxis.scaling==="logarithmic"&&(this.options.grid.minorVerticalLines=!0),this.options.grid.minorHorizontalLines===null&&this.options.yaxis.scaling==="logarithmic"&&(this.options.grid.minorHorizontalLines=!0),t.fire(this.el,"flotr:afterinitoptions",[this]),this.axes=r.Axis.getAxes(this.options);var s=[],o=[],u=this.series.length,a=this.series.length,f=this.options.colors,l=[],c=0,h,p,d,v;for(p=a-1;p>-1;--p)h=this.series[p].color,h&&(--a,n.isNumber(h)?s.push(h):l.push(r.Color.parse(h)));for(p=s.length-1;p>-1;--p)a=Math.max(a,s[p]+1);for(p=0;o.length=f.length&&(p=0,++c)}for(p=0,d=0;p10?t.minorTickFreq=0:o-u>5?t.minorTickFreq=2:t.minorTickFreq=5)}else e.tickSize=Flotr.getTickSize(t.noTicks,n,r,t.tickDecimals);e.min=n,e.max=r,t.min===null&&t.autoscale&&(e.min-=e.tickSize*i,e.min<0&&e.datamin>=0&&(e.min=0),e.min=e.tickSize*Math.floor(e.min/e.tickSize)),t.max===null&&t.autoscale&&(e.max+=e.tickSize*i,e.max>0&&e.datamax<=0&&e.datamax!=e.datamin&&(e.max=0),e.max=e.tickSize*Math.ceil(e.max/e.tickSize)),e.min==e.max&&(e.max=e.min+1)},calculateTextDimensions:function(e,t){var n="",r,i;if(this.options.showLabels)for(i=0;in.length&&(n=this.ticks[i].label);this.maxLabel=e.dimensions(n,{size:t.fontSize,angle:Flotr.toRad(this.options.labelsAngle)},"font-size:smaller;","flotr-grid-label"),this.titleSize=e.dimensions(this.options.title,{size:t.fontSize*1.2,angle:Flotr.toRad(this.options.titleAngle)},"font-weight:bold;","flotr-axis-title")},_cleanUserTicks:function(t,n){var r=this,i=this.options,s,o,u,a;e.isFunction(t)&&(t=t({min:r.min,max:r.max}));for(o=0;o1?a[1]:i.tickFormatter(s,{min:r.min,max:r.max})):(s=a,u=i.tickFormatter(s,{min:this.min,max:this.max})),n[o]={v:s,label:u}},_calculateTimeTicks:function(){this.ticks=Flotr.Date.generator(this)},_calculateLogTicks:function(){var e=this,t=e.options,n,r,s=Math.log(e.max);t.base!=Math.E&&(s/=Math.log(t.base)),s=Math.ceil(s);var o=Math.log(e.min);t.base!=Math.E&&(o/=Math.log(t.base)),o=Math.ceil(o);for(i=o;ii&&(i=a,o=!0)),f!==null&&(fs&&(s=f,u=!0));return{xmin:n,xmax:i,ymin:r,ymax:s,xused:o,yused:u}}},e.extend(t,{getSeries:function(n){return e.map(n,function(n){var r;return n.data?(r=new t,e.extend(r,n)):r=new t({data:n}),r})}}),Flotr.Series=t}(),Flotr.addType("lines",{options:{show:!1,lineWidth:2,fill:!1,fillBorder:!1,fillColor:null,fillOpacity:.4,steps:!1,stacked:!1},stack:{values:[]},draw:function(e){var t=e.context,n=e.lineWidth,r=e.shadowSize,i;t.save(),t.lineJoin="round",r&&(t.lineWidth=r/2,i=n/2+t.lineWidth/2,t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,i+r/2,!1),t.strokeStyle="rgba(0,0,0,0.2)",this.plot(e,i,!1)),t.lineWidth=n,t.strokeStyle=e.color,this.plot(e,0,!0),t.restore()},plot:function(e,t,n){function S(){!t&&e.fill&&d&&(v=o(d[0]),r.fillStyle=e.fillStyle,r.lineTo(m,p),r.lineTo(v,p),r.lineTo(v,u(d[1])),r.fill(),e.fillBorder&&r.stroke())}var r=e.context,i=e.width,s=e.height,o=e.xScale,u=e.yScale,a=e.data,f=e.stacked?this.stack:!1,l=a.length-1,c=null,h=null,p=u(0),d=null,v,m,g,y,b,w,E;if(l<1)return;r.beginPath();for(E=0;E0&&a[E][1]!==null&&(r.stroke(),S(),d=null,r.closePath(),r.beginPath());continue}v=o(a[E][0]),m=o(a[E+1][0]),d===null&&(d=a[E]),f?(b=f.values[a[E][0]]||0,w=f.values[a[E+1][0]]||f.values[a[E][0]]||0,g=u(a[E][1]+b),y=u(a[E+1][1]+w),n&&(a[E].y0=b,f.values[a[E][0]]=a[E][1]+b,E==l-1&&(a[E+1].y0=w,f.values[a[E+1][0]]=a[E+1][1]+w))):(g=u(a[E][1]),y=u(a[E+1][1]));if(g>s&&y>s||g<0&&y<0||v<0&&m<0||v>i&&m>i)continue;(c!=v||h!=g+t)&&r.moveTo(v,g+t),c=m,h=y+t,e.steps?(r.lineTo(c+t/2,g+t),r.lineTo(c+t/2,h)):r.lineTo(c,h)}(!e.fill||e.fill&&!e.fillBorder)&&r.stroke(),S(),r.closePath()},extendYRange:function(e,t,n,r){var i=e.options;if(n.stacked&&(!i.max&&i.max!==0||!i.min&&i.min!==0)){var s=e.max,o=e.min,u=r.positiveSums||{},a=r.negativeSums||{},f,l;for(l=0;l0?(u[f]=(u[f]||0)+t[l][1],s=Math.max(s,u[f])):(a[f]=(a[f]||0)+t[l][1],o=Math.min(o,a[f]));r.negativeSums=a,r.positiveSums=u,e.max=s,e.min=o}n.steps&&(this.hit=function(e){var t=e.data,n=e.args,r=e.yScale,i=n[0],s=t.length,o=n[1],u=e.xInverse(i.relX),a=i.relY,f;for(f=0;f=t[f][0]&&u<=t[f+1][0]){Math.abs(r(t[f][1])-a)<8&&(o.x=t[f][0],o.y=t[f][1],o.index=f,o.seriesIndex=e.index);break}},this.drawHit=function(e){var t=e.context,n=e.args,r=e.data,i=e.xScale,s=n.index,o=i(n.x),u=e.yScale(n.y),a;r.length-1>s&&(a=e.xScale(r[s+1][0]),t.save(),t.strokeStyle=e.color,t.lineWidth=e.lineWidth,t.beginPath(),t.moveTo(o,u),t.lineTo(a,u),t.stroke(),t.closePath(),t.restore())},this.clearHit=function(e){var t=e.context,n=e.args,r=e.data,i=e.xScale,s=e.lineWidth,o=n.index,u=i(n.x),a=e.yScale(n.y),f;r.length-1>o&&(f=e.xScale(r[o+1][0]),t.clearRect(u-s,a-s,f-u+2*s,2*s))})}}),Flotr.addType("bars",{options:{show:!1,lineWidth:2,barWidth:1,fill:!0,fillColor:null,fillOpacity:.4,horizontal:!1,stacked:!1,centered:!0,topPadding:.1,grouped:!1},stack:{positive:[],negative:[],_positive:[],_negative:[]},draw:function(e){var t=e.context;this.current+=1,t.save(),t.lineJoin="miter",t.lineWidth=e.lineWidth,t.strokeStyle=e.color,e.fill&&(t.fillStyle=e.fillStyle),this.plot(e),t.restore()},plot:function(e){var t=e.data,n=e.context,r=e.shadowSize,i,s,o,u,a,f;if(t.length<1)return;this.translate(n,e.horizontal);for(i=0;i0?o.positive:o.negative,p=d[c]||p,d[c]=p+h),v=f(c-a),m=f(c+i-a),g=l(h+p),y=l(p),y<0&&(y=0),e===null||t===null?null:{x:c,y:h,xScale:f,yScale:l,top:g,left:Math.min(v,m)-u/2,width:Math.abs(m-v)-u,height:y-g}},hit:function(e){var t=e.data,n=e.args,r=n[0],i=n[1],s=e.xInverse(r.relX),o=e.yInverse(r.relY),u=this.getBarGeometry(s,o,e),a=u.width/2,f=u.left,l=u.y,c,h;for(h=t.length;h--;)c=this.getBarGeometry(t[h][0],t[h][1],e),(l>0&&lc.y)&&Math.abs(f-c.left)0?(f[c]=(f[c]||0)+h,o=Math.max(o,f[c])):(l[c]=(l[c]||0)+h,s=Math.min(s,l[c]));(a==1&&u||a==-1&&!u)&&n.topPadding&&(e.max===e.datamax||n.stacked&&this.stackMax!==o)&&(o+=n.topPadding*(o-s)),this.stackMin=s,this.stackMax=o,this.negativeSums=l,this.positiveSums=f,e.max=o,e.min=s}}),Flotr.addType("bubbles",{options:{show:!1,lineWidth:2,fill:!0,fillOpacity:.4,baseRadius:2},draw:function(e){var t=e.context,n=e.shadowSize;t.save(),t.lineWidth=e.lineWidth,t.fillStyle="rgba(0,0,0,0.05)",t.strokeStyle="rgba(0,0,0,0.05)",this.plot(e,n/2),t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,n/4),t.strokeStyle=e.color,t.fillStyle=e.fillStyle,this.plot(e),t.restore()},plot:function(e,t){var n=e.data,r=e.context,i,s,o,u,a;t=t||0;for(s=0;sg?"downFillColor":"upFillColor"],e.fill&&!e.barcharts&&(n.fillStyle="rgba(0,0,0,0.05)",n.fillRect(y+o,x+o,b-y,S-x),n.save(),n.globalAlpha=e.fillOpacity,n.fillStyle=l,n.fillRect(y,x+u,b-y,S-x),n.restore());if(u||a)h=Math.floor((y+b)/2)+f,n.strokeStyle=l,n.beginPath(),e.barcharts?(n.moveTo(h,Math.floor(E+u)),n.lineTo(h,Math.floor(w+u)),T=dv&&ag&&fs.max||yo.max)continue;vs.max&&(m=s.max,s.lastSerie!=e&&(p=!1)),go.max&&(y=o.max,o.lastSerie!=e&&(p=!1)),r&&(u.beginPath(),u.moveTo(s.d2p(v),o.d2p(g)+n),u.lineTo(s.d2p(v),o.d2p(y)+n),u.lineTo(s.d2p(m),o.d2p(y)+n),u.lineTo(s.d2p(m),o.d2p(g)+n),u.fill(),u.closePath()),e.gantt.lineWidth&&(h||d||p)&&(u.beginPath(),u.moveTo(s.d2p(v),o.d2p(g)+n),u[h?"lineTo":"moveTo"](s.d2p(v),o.d2p(y)+n),u[p?"lineTo":"moveTo"](s.d2p(m),o.d2p(y)+n),u[d?"lineTo":"moveTo"](s.d2p(m),o.d2p(g)+n),u.stroke(),u.closePath())}},plotShadows:function(e,t,n){var r=e.data;if(r.length<1)return;var i,s,o,u,a=e.xaxis,f=e.yaxis,l=this.ctx,c=this.options.shadowSize;for(i=0;ia.max||vf.max)continue;ha.max&&(p=a.max),df.max&&(v=f.max);var m=a.d2p(p)-a.d2p(h)-(a.d2p(p)+c<=this.plotWidth?0:c),g=f.d2p(d)-f.d2p(v)-(f.d2p(d)+c<=this.plotHeight?0:c);l.fillStyle="rgba(0,0,0,0.05)",l.fillRect(Math.min(a.d2p(h)+c,this.plotWidth),Math.min(f.d2p(v)+c,this.plotHeight),m,g)}},extendXRange:function(e){if(e.options.max===null){var t=e.min,n=e.max,r,i,s,o,u,a={},f={},l=null;for(r=0;rt&&(t=e.max+o.barWidth)}}e.lastSerie=f,e.max=t,e.min=n,e.tickSize=Flotr.getTickSize(e.options.noTicks,n,t,e.options.tickDecimals)}}}),function(){function e(e){return typeof e=="object"&&e.constructor&&(Image?!0:e.constructor===Image)}Flotr.defaultMarkerFormatter=function(e){return Math.round(e.y*100)/100+""},Flotr.addType("markers",{options:{show:!1,lineWidth:1,color:"#000000",fill:!1,fillColor:"#FFFFFF",fillOpacity:.4,stroke:!1,position:"ct",verticalMargin:0,labelFormatter:Flotr.defaultMarkerFormatter,fontSize:Flotr.defaultOptions.fontSize,stacked:!1,stackingType:"b",horizontal:!1},stack:{positive:[],negative:[],values:[]},draw:function(e){function h(e,t){return o=r.negative[e]||0,s=r.positive[e]||0,t>0?(r.positive[e]=o+t,o+t):(r.negative[e]=s+t,s+t)}var t=e.data,n=e.context,r=e.stacked?e.stack:!1,i=e.stackingType,s,o,u,a,f,l,c;n.save(),n.lineJoin="round",n.lineWidth=e.lineWidth,n.strokeStyle="rgba(0,0,0,0.5)",n.fillStyle=e.fillStyle;for(a=0;a0?"top":"bottom",C,k,L;n.save(),n.translate(u/2,o/2),n.scale(1,v),k=Math.cos(b)*a,L=Math.sin(b)*a,i>0&&(this.plotSlice(k+i,L+i,h,g,y,n),l&&(n.fillStyle="rgba(0,0,0,0.1)",n.fill())),this.plotSlice(k,L,h,g,y,n),l&&(n.fillStyle=c,n.fill()),n.lineWidth=r,n.strokeStyle=f,n.stroke(),C={size:e.fontSize*1.2,color:e.fontColor,weight:1.5},w&&(e.htmlText||!e.textEnabled?(divStyle="position:absolute;"+N+":"+(o/2+(N==="top"?x:-x))+"px;",divStyle+=T+":"+(u/2+(T==="right"?-S:S))+"px;",d.push('
    ',w,"
    ")):(C.textAlign=T,C.textBaseline=N,Flotr.drawText(n,w,S,x,C)));if(e.htmlText||!e.textEnabled){var A=Flotr.DOM.node('
    ');Flotr.DOM.insert(A,d.join("")),Flotr.DOM.insert(e.element,A)}n.restore(),this.startAngle=y,this.slices=this.slices||[],this.slices.push({radius:h,x:k,y:L,explode:a,start:g,end:y})},plotSlice:function(e,t,n,r,i,s){s.beginPath(),s.moveTo(e,t),s.arc(e,t,n,r,i,!1),s.lineTo(e,t),s.closePath()},hit:function(e){var t=e.data[0],n=e.args,r=e.index,i=n[0],s=n[1],o=this.slices[r],u=i.relX-e.width/2,a=i.relY-e.height/2,f=Math.sqrt(u*u+a*a),l=Math.atan(a/u),c=Math.PI*2,h=o.explode||e.explode,p=o.start%c,d=o.end%c,v=e.epsilon;u<0?l+=Math.PI:u>0&&a<0&&(l+=c),fh&&(l>p&&ld&&(lp)||p===d&&(o.start===o.end&&Math.abs(l-p)v))&&(s.x=t[0],s.y=t[1],s.sAngle=p,s.eAngle=d,s.index=0,s.seriesIndex=r,s.fraction=t[1]/this.total)},drawHit:function(e){var t=e.context,n=this.slices[e.args.seriesIndex];t.save(),t.translate(e.width/2,e.height/2),this.plotSlice(n.x,n.y,n.radius,n.start,n.end,t),t.stroke(),t.restore()},clearHit:function(e){var t=e.context,n=this.slices[e.args.seriesIndex],r=2*e.lineWidth,i=n.radius+r;t.save(),t.translate(e.width/2,e.height/2),t.clearRect(n.x-i,n.y-i,2*i+r,2*i+r),t.restore()},extendYRange:function(e,t){this.total=(this.total||0)+t[0][1]}})}(),Flotr.addType("points",{options:{show:!1,radius:3,lineWidth:2,fill:!0,fillColor:"#FFFFFF",fillOpacity:1,hitRadius:null},draw:function(e){var t=e.context,n=e.lineWidth,r=e.shadowSize;t.save(),r>0&&(t.lineWidth=r/2,t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,r/2+t.lineWidth/2),t.strokeStyle="rgba(0,0,0,0.2)",this.plot(e,t.lineWidth/2)),t.lineWidth=e.lineWidth,t.strokeStyle=e.color,e.fill&&(t.fillStyle=e.fillStyle),this.plot(e),t.restore()},plot:function(e,t){var n=e.data,r=e.context,i=e.xScale,s=e.yScale,o,u,a;for(o=n.length-1;o>-1;--o){a=n[o][1];if(a===null)continue;u=i(n[o][0]),a=s(a);if(u<0||u>e.width||a<0||a>e.height)continue;r.beginPath(),t?r.arc(u,a+t,e.radius,0,Math.PI,!1):(r.arc(u,a,e.radius,0,2*Math.PI,!0),e.fill&&r.fill()),r.stroke(),r.closePath()}}}),Flotr.addType("radar",{options:{show:!1,lineWidth:2,fill:!0,fillOpacity:.4,radiusRatio:.9,sensibility:2},draw:function(e){var t=e.context,n=e.shadowSize;t.save(),t.translate(e.width/2,e.height/2),t.lineWidth=e.lineWidth,t.fillStyle="rgba(0,0,0,0.05)",t.strokeStyle="rgba(0,0,0,0.05)",this.plot(e,n/2),t.strokeStyle="rgba(0,0,0,0.1)",this.plot(e,n/4),t.strokeStyle=e.color,t.fillStyle=e.fillStyle,this.plot(e),t.restore()},plot:function(e,t){var n=e.data,r=e.context,i=Math.min(e.height,e.width)*e.radiusRatio/2,s=2*Math.PI/n.length,o=-Math.PI/2,u,a;t=t||0,r.beginPath();for(u=0;uthis.plotWidth||t.relY>this.plotHeight){this.el.style.cursor=null,e.removeClass(this.el,"flotr-crosshair");return}r.hideCursor&&(this.el.style.cursor="none",e.addClass(this.el,"flotr-crosshair")),n.save(),n.strokeStyle=r.color,n.lineWidth=1,n.beginPath(),r.mode.indexOf("x")!=-1&&(n.moveTo(s,i.top),n.lineTo(s,i.top+this.plotHeight)),r.mode.indexOf("y")!=-1&&(n.moveTo(i.left,o),n.lineTo(i.left+this.plotWidth,o)),n.stroke(),n.restore()},clearCrosshair:function(){var e=this.plotOffset,t=this.lastMousePos,n=this.octx;t&&(n.clearRect(Math.round(t.relX)+e.left,e.top,1,this.plotHeight+1),n.clearRect(e.left,Math.round(t.relY)+e.top,this.plotWidth+1,1))}})}(),function(){function n(e,t,n,r,i,s){var o="image/"+e,u=n.getImageData(0,0,r,i),a=new Image;return n.save(),n.globalCompositeOperation="destination-over",n.fillStyle=s,n.fillRect(0,0,r,i),a.src=t.toDataURL(o),n.restore(),n.clearRect(0,0,r,i),n.putImageData(u,0,0),a}var e=Flotr.DOM,t=Flotr._;Flotr.addPlugin("download",{saveImage:function(r,i,s,o){var u=this.options.grid,a;if(Flotr.isIE&&Flotr.isIE<9)return a=""+this.canvas.firstChild.innerHTML+"",window.open().document.write(a);if(r!=="jpeg"&&r!=="png")return;a=n(r,this.canvas,this.ctx,this.canvasWidth,this.canvasHeight,u&&u.backgroundColor||"#ffffff");if(!t.isElement(a)||!o)return window.open(a.src);this.download.restoreCanvas(),e.hide(this.canvas),e.hide(this.overlay),e.setStyles({position:"absolute"}),e.insert(this.el,a),this.saveImageElement=a},restoreCanvas:function(){e.show(this.canvas),e.show(this.overlay),this.saveImageElement&&this.el.removeChild(this.saveImageElement),this.saveImageElement=null}})}(),function(){var e=Flotr.EventAdapter,t=Flotr._;Flotr.addPlugin("graphGrid",{callbacks:{"flotr:beforedraw":function(){this.graphGrid.drawGrid()},"flotr:afterdraw":function(){this.graphGrid.drawOutline()}},drawGrid:function(){function v(e){for(p=0;p=c.max||(e==c.min||e==c.max)&&i.outlineWidth)return;r(Math.floor(c.d2p(e))+n.lineWidth/2)})}function g(e){n.moveTo(e,0),n.lineTo(e,f)}function y(e){n.moveTo(0,e),n.lineTo(l,e)}var n=this.ctx,r=this.options,i=r.grid,s=i.verticalLines,o=i.horizontalLines,u=i.minorVerticalLines,a=i.minorHorizontalLines,f=this.plotHeight,l=this.plotWidth,c,h,p,d;(s||u||o||a)&&e.fire(this.el,"flotr:beforegrid",[this.axes.x,this.axes.y,r,this]),n.save(),n.lineWidth=1,n.strokeStyle=i.tickColor;if(i.circular){n.translate(this.plotOffset.left+l/2,this.plotOffset.top+f/2);var b=Math.min(f,l)*r.radar.radiusRatio/2,w=this.axes.x.ticks.length,S=2*(Math.PI/w),x=-Math.PI/2;n.beginPath(),c=this.axes.y,o&&v(c.ticks),a&&v(c.minorTicks),s&&t.times(w,function(e){n.moveTo(0,0),n.lineTo(Math.cos(e*S+x)*b,Math.sin(e*S+x)*b)}),n.stroke()}else n.translate(this.plotOffset.left,this.plotOffset.top),i.backgroundColor&&(n.fillStyle=this.processColor(i.backgroundColor,{x1:0,y1:0,x2:l,y2:f}),n.fillRect(0,0,l,f)),n.beginPath(),c=this.axes.x,s&&m(c.ticks,g),u&&m(c.minorTicks,g),c=this.axes.y,o&&m(c.ticks,y),a&&m(c.minorTicks,y),n.stroke();n.restore(),(s||u||o||a)&&e.fire(this.el,"flotr:aftergrid",[this.axes.x,this.axes.y,r,this])},drawOutline:function(){var e=this,t=e.options,n=t.grid,r=n.outline,s=e.ctx,o=n.backgroundImage,u=e.plotOffset,a=u.left,f=u.top,l=e.plotWidth,c=e.plotHeight,h,p,d,v,m,g;if(!n.outlineWidth)return;s.save();if(n.circular){s.translate(a+l/2,f+c/2);var y=Math.min(c,l)*t.radar.radiusRatio/2,b=this.axes.x.ticks.length,w=2*(Math.PI/b),E=-Math.PI/2;s.beginPath(),s.lineWidth=n.outlineWidth,s.strokeStyle=n.color,s.lineJoin="round";for(i=0;i<=b;++i)s[i===0?"moveTo":"lineTo"](Math.cos(i*w+E)*y,Math.sin(i*w+E)*y);s.stroke()}else{s.translate(a,f);var S=n.outlineWidth,x=.5-S+(S+1)%2/2,T="lineTo",N="moveTo";s.lineWidth=S,s.strokeStyle=n.color,s.lineJoin="miter",s.beginPath(),s.moveTo(x,x),l-=S/2%1,c+=S/2,s[r.indexOf("n")!==-1?T:N](l,x),s[r.indexOf("e")!==-1?T:N](l,c),s[r.indexOf("s")!==-1?T:N](x,c),s[r.indexOf("w")!==-1?T:N](x,x),s.stroke(),s.closePath()}s.restore(),o&&(d=o.src||o,v=(parseInt(o.left,10)||0)+u.left,m=(parseInt(o.top,10)||0)+u.top,p=new Image,p.onload=function(){s.save(),o.alpha&&(s.globalAlpha=o.alpha),s.globalCompositeOperation="destination-over",s.drawImage(p,0,0,p.width,p.height,v,m,l,c),s.restore()},p.src=d)}})}(),function(){var e=Flotr.DOM,t=Flotr._,n=Flotr,r="opacity:0.7;background-color:#000;color:#fff;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;";Flotr.addPlugin("hit",{callbacks:{"flotr:mousemove":function(e,t){this.hit.track(t)},"flotr:click":function(e){var n=this.hit.track(e);n&&!t.isUndefined(n.index)&&(e.hit=n)},"flotr:mouseout":function(e){e.relatedTarget!==this.mouseTrack&&this.hit.clearHit()},"flotr:destroy":function(){this.options.mouse.container&&e.remove(this.mouseTrack),this.mouseTrack=null}},track:function(e){if(this.options.mouse.track||t.any(this.series,function(e){return e.mouse&&e.mouse.track}))return this.hit.hit(e)},executeOnType:function(e,r,i){function u(e,u){t.each(t.keys(n.graphTypes),function(t){e[t]&&e[t].show&&!e.hide&&this[t][r]&&(o=this.getOptions(e,t),o.fill=!!e.mouse.fillColor,o.fillStyle=this.processColor(e.mouse.fillColor||"#ffffff",{opacity:e.mouse.fillOpacity}),o.color=e.mouse.lineColor,o.context=this.octx,o.index=u,i&&(o.args=i),this[t][r].call(this[t],o),s=!0)},this)}var s=!1,o;return t.isArray(e)||(e=[e]),t.each(e,u,this),s},drawHit:function(e){var t=this.octx,n=e.series;if(n.mouse.lineColor){t.save(),t.lineWidth=n.points?n.points.lineWidth:1,t.strokeStyle=n.mouse.lineColor,t.fillStyle=this.processColor(n.mouse.fillColor||"#ffffff",{opacity:n.mouse.fillOpacity}),t.translate(this.plotOffset.left,this.plotOffset.top);if(!this.hit.executeOnType(n,"drawHit",e)){var r=e.xaxis,i=e.yaxis;t.beginPath(),t.arc(r.d2p(e.x),i.d2p(e.y),n.points.hitRadius||n.points.radius||n.mouse.radius,0,2*Math.PI,!0),t.fill(),t.stroke(),t.closePath()}t.restore(),this.clip(t)}this.prevHit=e},clearHit:function(){var t=this.prevHit,n=this.octx,r=this.plotOffset;n.save(),n.translate(r.left,r.top);if(t){if(!this.hit.executeOnType(t.series,"clearHit",this.prevHit)){var i=t.series,s=i.points?i.points.lineWidth:1;offset=(i.points.hitRadius||i.points.radius||i.mouse.radius)+s,n.clearRect(t.xaxis.d2p(t.x)-offset,t.yaxis.d2p(t.y)-offset,offset*2,offset*2)}e.hide(this.mouseTrack),this.prevHit=null}n.restore()},hit:function(e){var n=this.options,r=this.prevHit,i,s,o,u,a,f,l,c,h;if(this.series.length===0)return;h={relX:e.relX,relY:e.relY,absX:e.absX,absY:e.absY,series:this.series};if(n.mouse.trackY&&!n.mouse.trackAll&&this.hit.executeOnType(this.series,"hit",[e,h])&&!t.isUndefined(h.seriesIndex))a=this.series[h.seriesIndex],h.series=a,h.mouse=a.mouse,h.xaxis=a.xaxis,h.yaxis=a.yaxis;else{i=this.hit.closest(e);if(i){i=n.mouse.trackY?i.point:i.x,u=i.seriesIndex,a=this.series[u],l=a.xaxis,c=a.yaxis,s=2*a.mouse.sensibility;if(n.mouse.trackAll||i.distanceXl.xaxis.max)continue;p=Math.abs(g-v),d=Math.abs(y-m),h=p*p+d*d,h
    '),this.mouseTrack=c,e.insert(b||this.el,c));if(!g||g<0)g=0;a&&a.toFixed&&(a=a.toFixed(g)),f&&f.toFixed&&(f=f.toFixed(g)),T=n.mouse.trackFormatter({x:a,y:f,series:n.series,index:n.index,nearest:n,fraction:n.fraction});if(t.isNull(T)||t.isUndefined(T)){e.hide(c);return}c.innerHTML=T,e.show(c);if(!o)return;x=e.size(c),b&&(S=e.position(this.el),w=S.top,E=S.left);if(!n.mouse.relative)i+="top:",o.charAt(0)=="n"?i+=w+u+m:o.charAt(0)=="s"&&(i+=w-u+m+this.plotHeight-x.height),i+="px;bottom:auto;left:",o.charAt(1)=="e"?i+=E-u+p+this.plotWidth-x.width:o.charAt(1)=="w"&&(i+=E+u+p),i+="px;right:auto;";else if(s.pie&&s.pie.show){var N={x:this.plotWidth/2,y:this.plotHeight/2},C=Math.min(this.canvasWidth,this.canvasHeight)*s.pie.sizeRatio/2,k=n.sAngle=5||Math.abs(e.second.y-e.first.y)>=5}})}(),function(){var e=Flotr.DOM;Flotr.addPlugin("labels",{callbacks:{"flotr:afterdraw":function(){this.labels.draw()}},draw:function(){function b(e,t,r){var i=r?t.minorTicks:t.ticks,s=t.orientation===1,u=t.n===1,l,h;l={color:t.options.color||d.grid.color,angle:Flotr.toRad(t.options.labelsAngle),textBaseline:"middle"};for(c=0;c(s?e.plotWidth:e.plotHeight))continue;Flotr.drawText(v,n.label,l(e,s,o,a),h(e,s,o,a),u),!s&&!o&&(v.save(),v.strokeStyle=u.color,v.beginPath(),v.moveTo(e.plotOffset.left+e.plotWidth-8,e.plotOffset.top+t.d2p(n.v)),v.lineTo(e.plotOffset.left+e.plotWidth,e.plotOffset.top+t.d2p(n.v)),v.stroke(),v.restore())}}function E(e,t){var r=t.orientation===1,i=t.n===1,o="",u,a,f,l=e.plotOffset;!r&&!i&&(v.save(),v.strokeStyle=t.options.color||d.grid.color,v.beginPath());if(t.options.showLabels&&(i?!0:t.used))for(c=0;c(r?e.canvasWidth:e.canvasHeight))continue;f=l.top+(r?(i?1:-1)*(e.plotHeight+d.grid.labelMargin):t.d2p(n.v)-t.maxLabel.height/2),u=r?l.left+t.d2p(n.v)-s/2:0,o="",c===0?o=" first":c===t.ticks.length-1&&(o=" last"),o+=r?" flotr-grid-label-x":" flotr-grid-label-y",h+=['
    '+n.label+"
    "].join(" "),!r&&!i&&(v.moveTo(l.left+e.plotWidth-8,l.top+t.d2p(n.v)),v.lineTo(l.left+e.plotWidth,l.top+t.d2p(n.v)))}}var t,n,r,i,s,o,u,a,f,l,c,h="",p=0,d=this.options,v=this.ctx,m=this.axes,g={size:d.fontSize};for(c=0;c-1;--p){if(!n[p].label||n[p].hide)continue;d=s.labelFormatter(n[p].label),E=Math.max(E,this._text.measureText(d,S).width)}var x=Math.round(m+y*3+E),T=Math.round(f*(y+g)+y);!h&&h!==0&&(h=.1);if(!i.HtmlText&&this.textEnabled&&!s.container){l.charAt(0)=="s"&&(w=r.top+this.plotHeight-(c+T)),l.charAt(0)=="c"&&(w=r.top+this.plotHeight/2-(c+T/2)),l.charAt(1)=="e"&&(b=r.left+this.plotWidth-(c+x)),v=this.processColor(s.backgroundColor,{opacity:h}),a.fillStyle=v,a.fillRect(b,w,x,T),a.strokeStyle=s.labelBoxBorderColor,a.strokeRect(Flotr.toPixel(b),Flotr.toPixel(w),x,T);var N=b+y,C=w+y;for(p=0;p":""),u=!0);var k=n[p],L=s.labelBoxWidth,A=s.labelBoxHeight;d=s.labelFormatter(k.label),v="background-color:"+(k.bars&&k.bars.show&&k.bars.fillColor&&k.bars.fill?k.bars.fillColor:k.color)+";",o.push('','
    ','
    ','
    ',"
    ","
    ","",'',d,"")}u&&o.push("");if(o.length>0){var O=''+o.join("")+"
    ";if(s.container)O=e.node(O),this.legend.markup=O,e.insert(s.container,O);else{var M={position:"absolute",zIndex:"2",border:"1px solid "+s.labelBoxBorderColor};l.charAt(0)=="n"?(M.top=c+r.top+"px",M.bottom="auto"):l.charAt(0)=="c"?(M.top=c+(this.plotHeight-T)/2+"px",M.bottom="auto"):l.charAt(0)=="s"&&(M.bottom=c+r.bottom+"px",M.top="auto"),l.charAt(1)=="e"?(M.right=c+r.right+"px",M.left="auto"):l.charAt(1)=="w"&&(M.left=c+r.left+"px",M.right="auto");var P=e.create("div"),H;P.className="flotr-legend",e.setStyles(P,M),e.insert(P,O),e.insert(this.el,P);if(!h)return;var B=s.backgroundColor||i.grid.backgroundColor||"#ffffff";t.extend(M,e.size(P),{backgroundColor:B,zIndex:"",border:""}),M.width+="px",M.height+="px",P=e.create("div"),P.className="flotr-legend-bg",e.setStyles(P,M),e.opacity(P,h),e.insert(P," "),e.insert(this.el,P)}}}}}})}(),function(){function e(e){if(this.options.spreadsheet.tickFormatter)return this.options.spreadsheet.tickFormatter(e);var t=n.find(this.axes.x.ticks,function(t){return t.v==e});return t?t.label:e}var t=Flotr.DOM,n=Flotr._;Flotr.addPlugin("spreadsheet",{options:{show:!1,tabGraphLabel:"Graph",tabDataLabel:"Data",toolbarDownload:"Download CSV",toolbarSelectAll:"Select all",csvFileSeparator:",",decimalSeparator:".",tickFormatter:null,initialTab:"graph"},callbacks:{"flotr:afterconstruct":function(){if(!this.options.spreadsheet.show)return;var e=this.spreadsheet,n=t.node('
    '),r=t.node('
    '+this.options.spreadsheet.tabGraphLabel+"
    "),i=t.node('
    '+this.options.spreadsheet.tabDataLabel+"
    "),s;e.tabsContainer=n,e.tabs={graph:r,data:i},t.insert(n,r),t.insert(n,i),t.insert(this.el,n),s=t.size(i).height+2,this.plotOffset.bottom+=s,t.setStyles(n,{top:this.canvasHeight-s+"px"}),this.observe(r,"click",function(){e.showTab("graph")}).observe(i,"click",function(){e.showTab("data")}),this.options.spreadsheet.initialTab!=="graph"&&e.showTab(this.options.spreadsheet.initialTab)}},loadDataGrid:function(){if(this.seriesData)return this.seriesData;var e=this.series,t={};return n.each(e,function(e,r){n.each(e.data,function(e){var n=e[0],s=e[1],o=t[n];if(o)o[r+1]=s;else{var u=[];u[0]=n,u[r+1]=s,t[n]=u}})}),this.seriesData=n.sortBy(t,function(e,t){return parseInt(t,10)}),this.seriesData},constructDataGrid:function(){if(this.spreadsheet.datagrid)return this.spreadsheet.datagrid;var r=this.series,i=this.spreadsheet.loadDataGrid(),s=[""],o,u,a,f=[''];f.push(""),n.each(r,function(e,t){f.push('"),s.push("")}),f.push(""),n.each(i,function(t){f.push(""),n.times(r.length+1,function(r){var i="td",s=t[r],o=n.isUndefined(s)?"":Math.round(s*1e5)/1e5;if(r===0){i="th";var u=e.call(this,o);u&&(o=u)}f.push("<"+i+(i=="th"?' scope="row"':"")+">"+o+"")},this),f.push("")},this),s.push(""),a=t.node(f.join("")),o=t.node('"),u=t.node('"),this.observe(o,"click",n.bind(this.spreadsheet.downloadCSV,this)).observe(u,"click",n.bind(this.spreadsheet.selectAllData,this));var l=t.node('
    ');t.insert(l,o),t.insert(l,u);var c=this.canvasHeight-t.size(this.spreadsheet.tabsContainer).height-2,h=t.node('
    ');return t.insert(h,l),t.insert(h,a),t.insert(this.el,h),this.spreadsheet.datagrid=a,this.spreadsheet.container=h,a},showTab:function(e){if(this.spreadsheet.activeTab===e)return;switch(e){case"graph":t.hide(this.spreadsheet.container),t.removeClass(this.spreadsheet.tabs.data,"selected"),t.addClass(this.spreadsheet.tabs.graph,"selected");break;case"data":this.spreadsheet.datagrid||this.spreadsheet.constructDataGrid(),t.show(this.spreadsheet.container),t.addClass(this.spreadsheet.tabs.data,"selected"),t.removeClass(this.spreadsheet.tabs.graph,"selected");break;default:throw"Illegal tab name: "+e}this.spreadsheet.activeTab=e},selectAllData:function(){if(this.spreadsheet.tabs){var e,t,n,r,i=this.spreadsheet.constructDataGrid();return this.spreadsheet.showTab("data"),setTimeout(function(){(n=i.ownerDocument)&&(r=n.defaultView)&&r.getSelection&&n.createRange&&(e=window.getSelection())&&e.removeAllRanges?(t=n.createRange(),t.selectNode(i),e.removeAllRanges(),e.addRange(t)):document.body&&document.body.createTextRange&&(t=document.body.createTextRange())&&(t.moveToElementText(i),t.select())},0),!0}return!1},downloadCSV:function(){var t="",r=this.series,i=this.options,s=this.spreadsheet.loadDataGrid(),o=encodeURIComponent(i.spreadsheet.csvFileSeparator);if(i.spreadsheet.decimalSeparator===i.spreadsheet.csvFileSeparator)throw"The decimal separator is the same as the column separator ("+i.spreadsheet.decimalSeparator+")";n.each(r,function(e,n){t+=o+'"'+(e.label||String.fromCharCode(65+n)).replace(/\"/g,'\\"')+'"'}),t+="%0D%0A",t+=n.reduce(s,function(t,n){var r=e.call(this,n[0])||"";r='"'+(r+"").replace(/\"/g,'\\"')+'"';var s=n.slice(1).join(o);return i.spreadsheet.decimalSeparator!=="."&&(s=s.replace(/\./g,i.spreadsheet.decimalSeparator)),t+r+o+s+"%0D%0A"},"",this),Flotr.isIE&&Flotr.isIE<9?(t=t.replace(new RegExp(o,"g"),decodeURIComponent(o)).replace(/%0A/g,"\n").replace(/%0D/g,"\r"),window.open().document.write(t)):window.open("data:text/csv,"+t)}})}(),function(){var e=Flotr.DOM;Flotr.addPlugin("titles",{callbacks:{"flotr:afterdraw":function(){this.titles.drawTitles()}},drawTitles:function(){var t,n=this.options,r=n.grid.labelMargin,i=this.ctx,s=this.axes;if(!n.HtmlText&&this.textEnabled){var o={size:n.fontSize,color:n.grid.color,textAlign:"center"};n.subtitle&&Flotr.drawText(i,n.subtitle,this.plotOffset.left+this.plotWidth/2,this.titleHeight+this.subtitleHeight-2,o),o.weight=1.5,o.size*=1.5,n.title&&Flotr.drawText(i,n.title,this.plotOffset.left+this.plotWidth/2,this.titleHeight-2,o),o.weight=1.8,o.size*=.8,s.x.options.title&&s.x.used&&(o.textAlign=s.x.options.titleAlign||"center",o.textBaseline="top",o.angle=Flotr.toRad(s.x.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.x.options.title,this.plotOffset.left+this.plotWidth/2,this.plotOffset.top+s.x.maxLabel.height+this.plotHeight+2*r,o)),s.x2.options.title&&s.x2.used&&(o.textAlign=s.x2.options.titleAlign||"center",o.textBaseline="bottom",o.angle=Flotr.toRad(s.x2.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.x2.options.title,this.plotOffset.left+this.plotWidth/2,this.plotOffset.top-s.x2.maxLabel.height-2*r,o)),s.y.options.title&&s.y.used&&(o.textAlign=s.y.options.titleAlign||"right",o.textBaseline="middle",o.angle=Flotr.toRad(s.y.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.y.options.title,this.plotOffset.left-s.y.maxLabel.width-2*r,this.plotOffset.top+this.plotHeight/2,o)),s.y2.options.title&&s.y2.used&&(o.textAlign=s.y2.options.titleAlign||"left",o.textBaseline="middle",o.angle=Flotr.toRad(s.y2.options.titleAngle),o=Flotr.getBestTextAlign(o.angle,o),Flotr.drawText(i,s.y2.options.title,this.plotOffset.left+this.plotWidth+s.y2.maxLabel.width+2*r,this.plotOffset.top+this.plotHeight/2,o))}else{t=[],n.title&&t.push('
    ',n.title,"
    "),n.subtitle&&t.push('
    ',n.subtitle,"
    "),t.push(""),t.push('
    '),s.x.options.title&&s.x.used&&t.push('
    ',s.x.options.title,"
    "),s.x2.options.title&&s.x2.used&&t.push('
    ',s.x2.options.title,"
    "),s.y.options.title&&s.y.used&&t.push('
    ',s.y.options.title,"
    "),s.y2.options.title&&s.y2.used&&t.push('
    ',s.y2.options.title,"
    "),t=t.join("");var u=e.create("div");e.setStyles({color:n.grid.color}),u.className="flotr-titles",e.insert(this.el,u),e.insert(u,t)}}})}(); diff --git a/p/themes/Origine/freshrss.css b/p/themes/Origine/freshrss.css index 495477a86..78adbeff0 100644 --- a/p/themes/Origine/freshrss.css +++ b/p/themes/Origine/freshrss.css @@ -898,3 +898,46 @@ input.extend { text-decoration: underline; } } + +.stat{ + border:1px solid #aaa; + border-radius:10px; + box-shadow:2px 2px 5px #aaa; + margin: 10px 0; +} +.stat h2{ + border-bottom:1px solid #aaa; + margin:0; + padding-left:5px; +} +.stat h2 + *{ + margin:5px; +} +.stat h2 + div + div{ + margin:0 5px 5px 5px; +} +.stat h2 + table{ + border-collapse:collapse; + width:calc(100% - 10px); +} +.stat h2 + table th{ + text-transform:capitalize; +} +.stat h2 + table td{ + text-align:center; +} +.stat h2 + table thead th{ + border-bottom:2px solid #aaa; +} +.stat h2 + table tbody tr *{ + border-bottom:1px solid #aaa; +} +.stat h2 + table tbody tr:last-child *{ + border-bottom:0; +} +.stat h2 + table tr *{ + border-left:2px solid #aaa; +} +.stat h2 + table tr *:first-child{ + border-left:0; +} \ No newline at end of file -- cgit v1.2.3 From bd6a7606c0d2364fd472f7903260a72f37b31f3b Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 25 Jan 2014 23:21:01 +0100 Subject: Intégration statistiques MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quelques corrections https://github.com/marienfressinaud/FreshRSS/pull/390 en particulier correction bug chargement asynchrone JavaScript Pour https://github.com/marienfressinaud/FreshRSS/issues/90 --- app/Controllers/indexController.php | 11 +++++------ app/i18n/en.php | 10 +++++----- app/i18n/fr.php | 10 +++++----- app/views/index/stats.phtml | 15 +++++++++++++-- 4 files changed, 28 insertions(+), 18 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 84ed992f9..898f2577c 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -6,14 +6,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { private $entryDAO; private $feedDAO; private $catDAO; - private $statsDAO; function __construct($router) { parent::__construct($router); $this->entryDAO = new FreshRSS_EntryDAO (); $this->feedDAO = new FreshRSS_FeedDAO (); $this->catDAO = new FreshRSS_CategoryDAO (); - $this->statsDAO = new FreshRSS_StatsDAO (); } public function indexAction () { @@ -202,11 +200,12 @@ class FreshRSS_index_Controller extends Minz_ActionController { } public function statsAction () { + $statsDAO = new FreshRSS_StatsDAO (); Minz_View::appendScript (Minz_Url::display ('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); - $this->view->repartition = $this->statsDAO->calculateEntryRepartition(); - $this->view->count = ($this->statsDAO->calculateEntryCount()); - $this->view->feedByCategory = $this->statsDAO->calculateFeedByCategory(); - $this->view->entryByCategory = $this->statsDAO->calculateEntryByCategory(); + $this->view->repartition = $statsDAO->calculateEntryRepartition(); + $this->view->count = ($statsDAO->calculateEntryCount()); + $this->view->feedByCategory = $statsDAO->calculateFeedByCategory(); + $this->view->entryByCategory = $statsDAO->calculateEntryByCategory(); } public function aboutAction () { diff --git a/app/i18n/en.php b/app/i18n/en.php index 963105f35..518c14bc8 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -22,7 +22,7 @@ return array ( 'subscription_management' => 'Subscriptions management', 'main_stream' => 'Main stream', - 'all_feeds' => 'All feeds', + 'all_feeds' => 'All feeds', 'favorite_feeds' => 'Favourites (%d)', 'not_read' => '%d unread', 'not_reads' => '%d unread', @@ -300,10 +300,10 @@ return array ( 'format_date' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y', 'format_date_hour' => '%s j\<\s\u\p\>S\<\/\s\u\p\> Y \a\t H\:i', - 'status_favorites' => 'favourites', - 'status_read' => 'read', - 'status_unread' => 'unread', - 'status_total' => 'total', + 'status_favorites' => 'favourites', + 'status_read' => 'read', + 'status_unread' => 'unread', + 'status_total' => 'total', 'stats_entry_repartition' => 'Entry repartition', 'stats_entry_per_day' => 'Entry per day (last 30 days)', diff --git a/app/i18n/fr.php b/app/i18n/fr.php index c8b03ef8b..efe4a08c4 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -22,7 +22,7 @@ return array ( 'subscription_management' => 'Gestion des abonnements', 'main_stream' => 'Flux principal', - 'all_feeds' => 'Tous les flux', + 'all_feeds' => 'Tous les flux', 'favorite_feeds' => 'Favoris (%d)', 'not_read' => '%d non lu', 'not_reads' => '%d non lus', @@ -300,10 +300,10 @@ return array ( 'format_date' => 'j %s Y', 'format_date_hour' => 'j %s Y \à H\:i', - 'status_favorites' => 'favoris', - 'status_read' => 'lus', - 'status_unread' => 'non lus', - 'status_total' => 'total', + 'status_favorites' => 'favoris', + 'status_read' => 'lus', + 'status_unread' => 'non lus', + 'status_total' => 'total', 'stats_entry_repartition' => 'Répartition des articles', 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', diff --git a/app/views/index/stats.phtml b/app/views/index/stats.phtml index ab9d0a881..b1f0b8eb3 100644 --- a/app/views/index/stats.phtml +++ b/app/views/index/stats.phtml @@ -58,6 +58,15 @@
    \ No newline at end of file +} +initStats(); + -- cgit v1.2.3 From 3930235b1868a10aeefb88e9e860cec0a4a604d3 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 25 Jan 2014 23:44:36 +0100 Subject: indexController : Charge les DAO uniquement lorsque nécessaire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Poursuit le raisonnement de https://github.com/marienfressinaud/FreshRSS/commit/bd6a7606c0d2364fd472f7903260a72f37b31f3b Petites corrections https://github.com/marienfressinaud/FreshRSS/issues/90 --- app/Controllers/indexController.php | 28 +++++++++++----------------- app/i18n/en.php | 8 ++++---- app/i18n/fr.php | 4 ++-- app/views/index/stats.phtml | 2 +- 4 files changed, 18 insertions(+), 24 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 898f2577c..74883962d 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -1,18 +1,7 @@ entryDAO = new FreshRSS_EntryDAO (); - $this->feedDAO = new FreshRSS_FeedDAO (); - $this->catDAO = new FreshRSS_CategoryDAO (); - } public function indexAction () { $output = Minz_Request::param ('output'); @@ -50,8 +39,11 @@ class FreshRSS_index_Controller extends Minz_ActionController { Minz_View::appendScript (Minz_Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); } - $this->view->cat_aside = $this->catDAO->listCategories (); - $this->view->nb_favorites = $this->entryDAO->countUnreadReadFavorites (); + $catDAO = new FreshRSS_CategoryDAO(); + $entryDAO = new FreshRSS_EntryDAO(); + + $this->view->cat_aside = $catDAO->listCategories (); + $this->view->nb_favorites = $entryDAO->countUnreadReadFavorites (); $this->view->currentName = ''; $this->view->get_c = ''; @@ -125,14 +117,14 @@ class FreshRSS_index_Controller extends Minz_ActionController { $keepHistoryDefault = $this->view->conf->keep_history_default; try { - $entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault); + $entries = $entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter, $date_min, $keepHistoryDefault); // Si on a récupéré aucun article "non lus" // on essaye de récupérer tous les articles if ($state === 'not_read' && empty($entries)) { Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG); $this->view->state = 'all'; - $entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault); + $entries = $entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter, $date_min, $keepHistoryDefault); } if (count($entries) <= $nb) { @@ -170,7 +162,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { case 'c': $cat = isset($this->view->cat_aside[$getId]) ? $this->view->cat_aside[$getId] : null; if ($cat === null) { - $cat = $this->catDAO->searchById ($getId); + $catDAO = new FreshRSS_CategoryDAO(); + $cat = $catDAO->searchById($getId); } if ($cat) { $this->view->currentName = $cat->name (); @@ -183,7 +176,8 @@ class FreshRSS_index_Controller extends Minz_ActionController { case 'f': $feed = FreshRSS_CategoryDAO::findFeed($this->view->cat_aside, $getId); if (empty($feed)) { - $feed = $this->feedDAO->searchById ($getId); + $feedDAO = new FreshRSS_FeedDAO(); + $feed = $feedDAO->searchById($getId); } if ($feed) { $this->view->currentName = $feed->name (); diff --git a/app/i18n/en.php b/app/i18n/en.php index 518c14bc8..70fd8a74b 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -305,8 +305,8 @@ return array ( 'status_unread' => 'unread', 'status_total' => 'total', - 'stats_entry_repartition' => 'Entry repartition', - 'stats_entry_per_day' => 'Entry per day (last 30 days)', - 'stats_feed_per_category' => 'Feed per category', - 'stats_entry_per_category' => 'Entry per category', + 'stats_entry_repartition' => 'Entries repartition', + 'stats_entry_per_day' => 'Entries per day (last 30 days)', + 'stats_feed_per_category' => 'Feeds per category', + 'stats_entry_per_category' => 'Entries per category', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index efe4a08c4..53645477b 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -307,6 +307,6 @@ return array ( 'stats_entry_repartition' => 'Répartition des articles', 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', - 'stats_feed_per_category' => 'Flux par categorie', - 'stats_entry_per_category' => 'Article par categorie', + 'stats_feed_per_category' => 'Flux par catégorie', + 'stats_entry_per_category' => 'Articles par catégorie', ); diff --git a/app/views/index/stats.phtml b/app/views/index/stats.phtml index b1f0b8eb3..e531a31dc 100644 --- a/app/views/index/stats.phtml +++ b/app/views/index/stats.phtml @@ -8,7 +8,7 @@
     '+(e.label||String.fromCharCode(65+t))+"
    - + -- cgit v1.2.3 From b0641fd0ebcaa02dcbeb2983703bff6516f93487 Mon Sep 17 00:00:00 2001 From: Alexis Degrugillier Date: Sun, 26 Jan 2014 08:54:52 -0500 Subject: Modification des statistiques. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajout de la liste des 10 plus gros contributeurs Ajout de règles CSS pour les thèmes Flat et Dark Modification de l'affichage des camemberts (tri par nombre qui remplace le tri alphabétique) --- app/Controllers/indexController.php | 1 + app/Models/StatsDAO.php | 29 +++++++++++++++++++++++- app/i18n/en.php | 4 ++++ app/i18n/fr.php | 4 ++++ app/views/index/stats.phtml | 37 +++++++++++++++++++++++------- p/themes/Dark/freshrss.css | 45 +++++++++++++++++++++++++++++++++++++ p/themes/Flat/freshrss.css | 45 +++++++++++++++++++++++++++++++++++++ p/themes/Origine/freshrss.css | 5 ++++- 8 files changed, 160 insertions(+), 10 deletions(-) (limited to 'app/Controllers') diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 74883962d..cb6be6049 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -200,6 +200,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { $this->view->count = ($statsDAO->calculateEntryCount()); $this->view->feedByCategory = $statsDAO->calculateFeedByCategory(); $this->view->entryByCategory = $statsDAO->calculateEntryByCategory(); + $this->view->topFeed = $statsDAO->calculateTopFeed(); } public function aboutAction () { diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index a317c8f27..60cec7847 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -122,6 +122,7 @@ FROM {$this->prefix}category AS c, {$this->prefix}feed AS f WHERE c.id = f.category GROUP BY label +ORDER BY data DESC SQL; $stm = $this->bd->prepare($sql); $stm->execute(); @@ -146,6 +147,7 @@ FROM {$this->prefix}category AS c, WHERE c.id = f.category AND f.id = e.id_feed GROUP BY label +ORDER BY data DESC SQL; $stm = $this->bd->prepare($sql); $stm->execute(); @@ -154,6 +156,31 @@ SQL; return $this->convertToPieSerie($res); } + /** + * Calculates the 10 top feeds based on their number of entries + * + * @return array + */ + public function calculateTopFeed() { + $sql = <<prefix}category AS c, +{$this->prefix}feed AS f, +{$this->prefix}entry AS e +WHERE c.id = f.category +AND f.id = e.id_feed +GROUP BY id +ORDER BY count DESC +LIMIT 10 +SQL; + $stm = $this->bd->prepare($sql); + $stm->execute(); + return $stm->fetchAll(PDO::FETCH_ASSOC); + } + private function convertToSerie($data) { $serie = array(); @@ -168,7 +195,7 @@ SQL; $serie = array(); foreach ($data as $value) { - $value['data'] = array(array(0, (int)$value['data'])); + $value['data'] = array(array(0, (int) $value['data'])); $serie[] = $value; } diff --git a/app/i18n/en.php b/app/i18n/en.php index 70fd8a74b..e5cf623c7 100644 --- a/app/i18n/en.php +++ b/app/i18n/en.php @@ -11,6 +11,8 @@ return array ( 'users' => 'Users', 'categories' => 'Categories', 'category' => 'Category', + 'feed' => 'Feed', + 'feeds' => 'Feeds', 'shortcuts' => 'Shortcuts', 'about' => 'About', 'stats' => 'Statistics', @@ -309,4 +311,6 @@ return array ( 'stats_entry_per_day' => 'Entries per day (last 30 days)', 'stats_feed_per_category' => 'Feeds per category', 'stats_entry_per_category' => 'Entries per category', + 'stats_top_feed' => 'Top ten feeds', + 'stats_entry_count' => 'Entry count', ); diff --git a/app/i18n/fr.php b/app/i18n/fr.php index 53645477b..693dce5d1 100644 --- a/app/i18n/fr.php +++ b/app/i18n/fr.php @@ -11,6 +11,8 @@ return array ( 'users' => 'Utilisateurs', 'categories' => 'Catégories', 'category' => 'Catégorie', + 'feed' => 'Flux', + 'feeds' => 'Flux', 'shortcuts' => 'Raccourcis', 'about' => 'À propos', 'stats' => 'Statistiques', @@ -309,4 +311,6 @@ return array ( 'stats_entry_per_day' => 'Nombre d’articles par jour (30 derniers jours)', 'stats_feed_per_category' => 'Flux par catégorie', 'stats_entry_per_category' => 'Articles par catégorie', + 'stats_top_feed' => 'Les dix plus gros flux', + 'stats_entry_count' => 'Nombre d’articles', ); diff --git a/app/views/index/stats.phtml b/app/views/index/stats.phtml index 487c50a4b..ae9db172b 100644 --- a/app/views/index/stats.phtml +++ b/app/views/index/stats.phtml @@ -16,23 +16,23 @@ - - + + - - + + - - + + - - + +
      
    repartition['main_stream']['total']?>repartition['all_feeds']['total']?>repartition['main_stream']['total']?>repartition['all_feeds']['total']?>
    repartition['main_stream']['read']?>repartition['all_feeds']['read']?>repartition['main_stream']['read']?>repartition['all_feeds']['read']?>
    repartition['main_stream']['unread']?>repartition['all_feeds']['unread']?>repartition['main_stream']['unread']?>repartition['all_feeds']['unread']?>
    repartition['main_stream']['favorite']?>repartition['all_feeds']['favorite']?>repartition['main_stream']['favorite']?>repartition['all_feeds']['favorite']?>
    @@ -55,6 +55,27 @@
    +
    +

    + + + + + + + + + + topFeed as $feed):?> + + + + + + + +
    +