public static function icon($name, $urlOnly = false) { static $alts = array('add' => '✚', 'all' => '☰', 'bookmark' => '★', 'category' => '☷', 'category-white' => '☷', 'close' => '❌', 'configure' => '⚙', 'down' => '▽', 'favorite' => '★', 'help' => 'ⓘ', 'link' => '↗', 'login' => '🔒', 'logout' => '🔓', 'next' => '⏩', 'non-starred' => '☆', 'prev' => '⏪', 'read' => '☑', 'unread' => '☐', 'refresh' => '🔃', 'search' => '🔍', 'share' => '♺', 'starred' => '★', 'tag' => '⚐', 'up' => '△'); if (!isset($alts[$name])) { return ''; } $url = $name . '.svg'; $url = isset(self::$themeIcons[$url]) ? self::$themeIconsUrl . $url : self::$defaultIconsUrl . $url; return $urlOnly ? Minz_Url::display($url) : '<img class="icon" src="' . Minz_Url::display($url) . '" alt="' . $alts[$name] . '" />'; }
/** * This action handles the main subscription page * * It displays categories and associated feeds. */ public function indexAction() { Minz_View::appendScript(Minz_Url::display('/scripts/category.js?' . @filemtime(PUBLIC_PATH . '/scripts/category.js'))); Minz_View::prependTitle(_t('sub.title') . ' · '); $id = Minz_Request::param('id'); if ($id !== false) { $feedDAO = FreshRSS_Factory::createFeedDao(); $this->view->feed = $feedDAO->searchById($id); } }
/** * This action displays the user profile page. */ public function profileAction() { Minz_View::prependTitle(_t('conf.profile.title') . ' · '); Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); if (Minz_Request::isPost()) { $ok = true; $passwordPlain = Minz_Request::param('newPasswordPlain', '', true); if ($passwordPlain != '') { Minz_Request::_param('newPasswordPlain'); //Discard plain-text password ASAP $_POST['newPasswordPlain'] = ''; if (!function_exists('password_hash')) { include_once LIB_PATH . '/password_compat.php'; } $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 != ''; FreshRSS_Context::$user_conf->passwordHash = $passwordHash; } Minz_Session::_param('passwordHash', FreshRSS_Context::$user_conf->passwordHash); $passwordPlain = Minz_Request::param('apiPasswordPlain', '', true); if ($passwordPlain != '') { if (!function_exists('password_hash')) { include_once LIB_PATH . '/password_compat.php'; } $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 != ''; FreshRSS_Context::$user_conf->apiPasswordHash = $passwordHash; } // TODO: why do we need of hasAccess here? if (FreshRSS_Auth::hasAccess('admin')) { FreshRSS_Context::$user_conf->mail_login = Minz_Request::param('mail_login', '', true); } $email = FreshRSS_Context::$user_conf->mail_login; Minz_Session::_param('mail', $email); $ok &= FreshRSS_Context::$user_conf->save(); if ($email != '') { $personaFile = DATA_PATH . '/persona/' . $email . '.txt'; @unlink($personaFile); $ok &= file_put_contents($personaFile, Minz_Session::param('currentUser', '_')) !== false; } if ($ok) { Minz_Request::good(_t('feedback.profile.updated'), array('c' => 'user', 'a' => 'profile')); } else { Minz_Request::bad(_t('feedback.profile.error'), array('c' => 'user', 'a' => 'profile')); } } }
/** * This action displays the global view of FreshRSS. */ public function globalAction() { $allow_anonymous = FreshRSS_Context::$system_conf->allow_anonymous; if (!FreshRSS_Auth::hasAccess() && !$allow_anonymous) { Minz_Request::forward(array('c' => 'auth', 'a' => 'login')); return; } Minz_View::appendScript(Minz_Url::display('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js'))); try { $this->updateContext(); } catch (FreshRSS_Context_Exception $e) { Minz_Error::error(404); } $this->view->categories = FreshRSS_Context::$categories; $this->view->rss_title = FreshRSS_Context::$name . ' | ' . Minz_View::title(); $title = _t('index.feed.title_global'); if (FreshRSS_Context::$get_unread > 0) { $title = '(' . FreshRSS_Context::$get_unread . ') ' . $title; } Minz_View::prependTitle($title . ' · '); }
/** * Return the url for a given file. * * @param $filename name of the file to serve. * @param $type the type (js or css) of the file to serve. * @return the url corresponding to the file. */ public function getFileUrl($filename, $type) { $dir = substr(strrchr($this->path, '/'), 1); $file_name_url = urlencode($dir . '/static/' . $filename); $absolute_path = $this->path . '/static/' . $filename; $mtime = @filemtime($absolute_path); $url = '/ext.php?f=' . $file_name_url . '&t=' . $type . '&' . $mtime; return Minz_Url::display($url, 'php'); }
/** * Relance une requête * @param $url l'url vers laquelle est relancée la requête * @param $redirect si vrai, force la redirection http * > sinon, le dispatcher recharge en interne */ public static function forward($url = array(), $redirect = false) { if (!is_array($url)) { header('Location: ' . $url); exit; } $url = Minz_Url::checkUrl($url); if ($redirect) { header('Location: ' . Minz_Url::display($url, 'php')); exit; } else { self::_controllerName($url['c']); self::_actionName($url['a']); self::_params(array_merge(self::$params, $url['params'])); Minz_Dispatcher::reset(); } }
/** * This action resets the authentication system. * * After reseting, form auth is set by default. */ public function resetAction() { Minz_View::prependTitle(_t('admin.auth.title_reset') . ' · '); Minz_View::appendScript(Minz_Url::display('/scripts/bcrypt.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/bcrypt.min.js'))); $this->view->no_form = false; // Enable changement of auth only if Persona! if (FreshRSS_Context::$system_conf->auth_type != 'persona') { $this->view->message = array('status' => 'bad', 'title' => _t('gen.short.damn'), 'body' => _t('feedback.auth.not_persona')); $this->view->no_form = true; return; } $conf = get_user_configuration(FreshRSS_Context::$system_conf->default_user); if (is_null($conf)) { return; } // Admin user must have set its master password. if (!$conf->passwordHash) { $this->view->message = array('status' => 'bad', 'title' => _t('gen.short.damn'), 'body' => _t('feedback.auth.no_password_set')); $this->view->no_form = true; return; } invalidateHttpCache(); if (Minz_Request::isPost()) { $nonce = Minz_Session::param('nonce'); $username = Minz_Request::param('username', ''); $challenge = Minz_Request::param('challenge', ''); $ok = FreshRSS_FormAuth::checkCredentials($username, $conf->passwordHash, $nonce, $challenge); if ($ok) { FreshRSS_Context::$system_conf->auth_type = 'form'; $ok = FreshRSS_Context::$system_conf->save(); if ($ok) { Minz_Request::good(_t('feedback.auth.form.set')); } else { Minz_Request::bad(_t('feedback.auth.form.not_set'), array('c' => 'auth', 'a' => 'reset')); } } else { Minz_Log::warning('Password mismatch for' . ' user='******', nonce=' . $nonce . ', c=' . $challenge); Minz_Request::bad(_t('feedback.auth.login.invalid'), array('c' => 'auth', 'a' => 'reset')); } } }
private function _queries(&$data, $values) { $data['queries'] = array(); foreach ($values as $value) { $value = array_filter($value); $params = $value; unset($params['name']); unset($params['url']); $value['url'] = Minz_Url::display(array('params' => $params)); $data['queries'][] = $value; } }
/** * This action handles the article repartition statistic page. * * It displays the number of article and the average of article for the * following periods: * - hour of the day * - day of the week * - month * * @todo verify that the metrics used here make some sense. Especially * for the average. */ public function repartitionAction() { $statsDAO = FreshRSS_Factory::createStatsDAO(); $categoryDAO = new FreshRSS_CategoryDAO(); $feedDAO = FreshRSS_Factory::createFeedDao(); Minz_View::appendScript(Minz_Url::display('/scripts/flotr2.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/flotr2.min.js'))); $id = Minz_Request::param('id', null); $this->view->categories = $categoryDAO->listCategories(); $this->view->feed = $feedDAO->searchById($id); $this->view->days = $statsDAO->getDays(); $this->view->months = $statsDAO->getMonths(); $this->view->repartition = $statsDAO->calculateEntryRepartitionPerFeed($id); $this->view->repartitionHour = $statsDAO->calculateEntryRepartitionPerFeedPerHour($id); $this->view->averageHour = $statsDAO->calculateEntryAveragePerFeedPerHour($id); $this->view->repartitionDayOfWeek = $statsDAO->calculateEntryRepartitionPerFeedPerDayOfWeek($id); $this->view->averageDayOfWeek = $statsDAO->calculateEntryAveragePerFeedPerDayOfWeek($id); $this->view->repartitionMonth = $statsDAO->calculateEntryRepartitionPerFeedPerMonth($id); $this->view->averageMonth = $statsDAO->calculateEntryAveragePerFeedPerMonth($id); }
/** * Add support of image lazy loading * Move content from src attribute to data-original * @param content is the text we want to parse */ function lazyimg($content) { return preg_replace('/<img([^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', '<img$1src="' . Minz_Url::display('/themes/icons/grey.gif') . '" data-original="$2"$3>', $content); }
private function loadStylesAndScripts() { $theme = FreshRSS_Themes::load(FreshRSS_Context::$user_conf->theme); if ($theme) { foreach ($theme['files'] as $file) { if ($file[0] === '_') { $theme_id = 'base-theme'; $filename = substr($file, 1); } else { $theme_id = $theme['id']; $filename = $file; } $filetime = @filemtime(PUBLIC_PATH . '/themes/' . $theme_id . '/' . $filename); Minz_View::appendStyle(Minz_Url::display('/themes/' . $theme_id . '/' . $filename . '?' . $filetime)); } } Minz_View::appendScript(Minz_Url::display('/scripts/jquery.min.js?' . @filemtime(PUBLIC_PATH . '/scripts/jquery.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'))); if (FreshRSS_Context::$system_conf->auth_type === 'persona') { // TODO move it in a plugin // Needed for login AND logout with Persona. Minz_View::appendScript('https://login.persona.org/include.js'); $file_mtime = @filemtime(PUBLIC_PATH . '/scripts/persona.js'); Minz_View::appendScript(Minz_Url::display('/scripts/persona.js?' . $file_mtime)); } }
public function favicon() { return Minz_Url::display('/f.php?' . $this->hash()); }
function _url($controller, $action) { $nb_args = func_num_args(); if ($nb_args < 2 || $nb_args % 2 != 0) { return false; } $args = func_get_args(); $params = array(); for ($i = 2; $i < $nb_args; $i = $i + 2) { $params[$args[$i]] = $args[$i + 1]; } return Minz_Url::display(array('c' => $controller, 'a' => $action, 'params' => $params)); }
private function loadStylesAndScripts($loginOk) { $theme = FreshRSS_Themes::load($this->conf->theme); if ($theme) { foreach ($theme['files'] as $file) { Minz_View::appendStyle(Minz_Url::display('/themes/' . $theme['id'] . '/' . $file . '?' . @filemtime(PUBLIC_PATH . '/themes/' . $theme['id'] . '/' . $file))); } } 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'))); }
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); $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'] = $reason == '' ? Minz_Translate::t('invalid_login') : $reason; Minz_Log::record('Persona: ' . $res['reason'], Minz_Log::WARNING); } header('Content-Type: application/json; charset=UTF-8'); $this->view->res = json_encode($res); }
/** * This action handles the creation of a user query. * * It gets the GET parameters and stores them in the configuration query * storage. Before it is saved, the unwanted parameters are unset to keep * lean data. */ public function addQueryAction() { $category_dao = FreshRSS_Factory::createCategoryDAO(); $feed_dao = FreshRSS_Factory::createFeedDao(); $queries = array(); foreach (FreshRSS_Context::$user_conf->queries as $key => $query) { $queries[$key] = new FreshRSS_UserQuery($query, $feed_dao, $category_dao); } $params = Minz_Request::params(); $params['url'] = Minz_Url::display(array('params' => $params)); $params['name'] = _t('conf.query.number', count($queries) + 1); $queries[] = new FreshRSS_UserQuery($params, $feed_dao, $category_dao); FreshRSS_Context::$user_conf->queries = $queries; FreshRSS_Context::$user_conf->save(); Minz_Request::good(_t('feedback.conf.query_created', $query['name']), array('c' => 'configure', 'a' => 'queries')); }