/** * Constructor * @param string $slug */ public function __construct($slug) { $Database = AppCore::GetDatabase(); $Cache = AppCore::GetMemcached(); $mckey = "railpage:news.article_slug=" . $slug; $loaded = false; if ($story_id = $Cache->fetch($mckey)) { try { parent::__construct($story_id); $loaded = true; } catch (Exception $e) { } } /** * Fall back to a database query if we can't load the news article from Memcached */ if (!$loaded) { $story_id = $Database->fetchOne("SELECT sid FROM nuke_stories WHERE slug = ?", $slug); if (filter_var($story_id, FILTER_VALIDATE_INT)) { $Cache->save($mckey, $story_id, strtotime("+6 months")); parent::__construct($story_id); } else { throw new Exception("Could not find a story matching URL slug " . $slug); return false; } } }
/** * Get a location ID from a URL slug * @since Version 3.10.0 * @param string $slug * @return int */ public static function getLocationId($slug) { $Redis = AppCore::GetRedis(); $Memcached = AppCore::GetMemcached(); $Database = (new AppCore())->getDatabaseConnection(); $key = sprintf("railpage:locations.slug=%s", $slug); if (!($id = $Memcached->fetch($key))) { $id = $Database->fetchOne("SELECT id FROM location WHERE slug = ?", $slug); $Memcached->save($key, $id, 0); } return $id; }
/** * Find a user ID from a given push registration ID * @since Version 3.10.0 * @param string $registration_id * @return int */ public static function getUserIdFromRegistrationId($registration_id) { $Database = AppCore::GetDatabase(); $Memcached = AppCore::GetMemcached(); $key = sprintf("railpage:gcm.subscription.key=%s", $registration_id); if (!($user_id = $Memcached->fetch($key))) { $query = "SELECT user_id FROM nuke_user_push WHERE registration_id = ?"; $user_id = $Database->fetchOne($query, $registration_id); $Memcached->save($key, $user_id, 0); } return $user_id; }
/** * Get a phpBB config item * @since Version 3.10.0 * @param string $key * @return mixed */ public static function getPhpBB($key = null) { $Memcached = AppCore::GetMemcached(); $cachekey = sprintf("railpage:config_phpbb:%s", $key); if ($rs = $Memcached->fetch($cachekey)) { return $rs; } $Database = AppCore::GetDatabase(); $query = "SELECT config_value FROM nuke_bbconfig WHERE config_name = 'allow_html_tags'"; $rs = $Database->fetchOne($query); $Memcached->save($cachekey, $rs, strtotime("+1 month")); return $rs; }
/** * Get the formatted lead text for this article * @since Version 3.10.0 * @param \Railpage\News\Article $Article * @param string $section The section of the article (lead or paragraphs) to format * @return string */ public static function FormatArticleText($Article, $section = "lead") { $Memcached = AppCore::GetMemcached(); $cachekey = $section == "lead" ? Article::CACHE_KEY_FORMAT_LEAD : Article::CACHE_KEY_FORMAT_PARAGRAPHS; $cachekey = sprintf($cachekey, $Article->id); $whitespace_find = array("<p> </p>", "<p></p>", "<p> </p>"); $whitespace_replace = array("", "", ""); #$Memcached->delete($cachekey); if (!($text = $Memcached->Fetch($cachekey))) { $text = $section == "lead" ? $Article->getLead() : $Article->getParagraphs(); if (function_exists("format_post")) { $text = str_replace($whitespace_find, $whitespace_replace, $text); $text = format_post($text); if (is_object($text)) { $text = $text->__toString(); } $Memcached->save($cachekey, $text, 0); } } return $text; }
/** * Get $num newest images * @since Version 3.10.0 * @param int $num * @return array * @todo $cacheProvider doesn't seem to bloody work! */ public static function getNewest($num = 5) { $cacheProvider = AppCore::GetMemcached(); $mckey = sprintf("railpage:images.recent=%d;url.cached", $num); if ($newphotos = $cacheProvider->fetch($mckey)) { Debug::LogCLI("Fetched new photos from cache provider using cache key " . $mckey); return $newphotos; } $newphotos = (new Images())->getRecentAdditions(5); shuffle($newphotos); foreach ($newphotos as $id => $data) { $newphotos[$id]['meta']['sizes']['medium']['source'] = ImageCache::cache($newphotos[$id]['meta']['sizes']['medium']['source']); } $rs = $cacheProvider->save($mckey, $newphotos, 900); // save for 15 minutes Debug::LogCLI("Saved new photos in cache provider using cache key " . $mckey); if ($res = $cacheProvider->fetch($mckey)) { Debug::LogCLI("new photos found in cache, success"); } return $newphotos; }
/** * Get the SVG string for a gaussian blur of an image thumbnail, blown up to full size * Displayed while the full image loads in the background * @since Version 3.10.0 * @param \Railpage\Images\Images $imageObject * @return string */ public static function GetLoadingSVG(Image $imageObject) { $cachekey = sprintf("railpage:base64.image.svg=%d", $imageObject->id); $Memcached = AppCore::GetMemcached(); // Check our base64 hash against a known, shitty hash, itself hashed in md5 $badhash = ["f8984b3824a761805223862ca156bf1e", "10a7bf41c903ba2b3fab231fc34e4637"]; $base64 = $Memcached->Fetch($cachekey); if (!$base64 || in_array(md5($base64), $badhash)) { /* global $User; if ($User->id == 45) { $base64 = $Memcached->Fetch($cachekey); //echo $base64;die; } if (!$base64 = $Memcached->Fetch($cachekey)) {*/ $thumbnail = $imageObject->sizes['thumb']['source']; $cached_url = ImageCache::cache($thumbnail); $base64 = base64_encode(file_get_contents($cached_url)); $Memcached->save($cachekey, $base64); } $dstw = $imageObject->sizes['largest']['width']; $dsth = $imageObject->sizes['largest']['height']; $string = ' <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' . $dstw . '" height="' . $dsth . '" viewBox="0 0 ' . $dstw . ' ' . $dsth . '"> <filter id="blur" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> <feGaussianBlur stdDeviation="20 20" edgeMode="duplicate" /> <feComponentTransfer> <feFuncA type="discrete" tableValues="1 1" /> </feComponentTransfer> </filter> <image filter="url(#blur)" xlink:href="data:image/jpeg;base64,' . $base64 . '" x="0" y="0" height="100%25" width="100%25"/> </svg>'; $find = [" ", "<", ">", "\"", ":", "(", ")", ";", ",", "#", "=", "\n"]; $replace = ["%20", "%3C", "%3E", "%22", "%3A", "%28", "%29", "%3B", "%2C", "%23", "%3D", "%0A"]; return "data:image/svg+xml;charset=utf-8," . str_replace($find, $replace, trim($string)); }
/** * Commit changes to this competition * @since Version 3.9.1 * @return \Railpage\Images\Competition */ public function commit() { $this->validate(); $data = array("title" => $this->title, "theme" => $this->theme, "description" => $this->description, "slug" => $this->slug, "status" => $this->status, "author" => $this->Author->id, "voting_date_open" => $this->VotingDateOpen instanceof DateTime ? $this->VotingDateOpen->format("Y-m-d H:i:s") : "0000-00-00 00:00:00", "voting_date_close" => $this->VotingDateClose instanceof DateTime ? $this->VotingDateClose->format("Y-m-d H:i:s") : "0000-00-00 00:00:00", "submissions_date_open" => $this->SubmissionsDateOpen instanceof DateTime ? $this->SubmissionsDateOpen->format("Y-m-d H:i:s") : "0000-00-00 00:00:00", "submissions_date_close" => $this->SubmissionsDateClose instanceof DateTime ? $this->SubmissionsDateClose->format("Y-m-d H:i:s") : "0000-00-00 00:00:00", "meta" => json_encode($this->meta)); if (filter_var($this->id, FILTER_VALIDATE_INT)) { $where = array("id = ?" => $this->id); $this->db->update("image_competition", $data, $where); } if (!filter_var($this->id, FILTER_VALIDATE_INT)) { $this->db->insert("image_competition", $data); $this->id = $this->db->lastInsertId(); } /** * Clear the cache */ $regkey = sprintf(self::CACHE_KEY, $this->id); $Redis = AppCore::GetRedis(); $Memcached = AppCore::GetMemcached(); $Redis->delete($regkey); $Memcached->delete($regkey); /** * Check our themes and see if we need to mark this theme as used */ $themes = (new Competitions())->getSuggestedThemes(); foreach ($themes as $key => $theme) { $theme['theme'] = ContentUtility::FormatTitle($theme['theme']); if ((!isset($theme['used']) || $theme['used'] === false) && $theme['theme'] === $this->theme) { $themes[$key]['used'] = true; } } $Config = new Config(); $Config->set("image.competition.suggestedthemes", json_encode($themes), "Photo competition themes"); $this->url = Utility\Url::makeCompetitionUrls($this); return $this; }
/** * Create a camera object from an ID or URL slug * @since Version 3.10.0 * @param string|int $id * @return \Railpage\Images\Camera */ public static function CreateCamera($id) { $Database = AppCore::GetDatabase(); $Memcached = AppCore::GetMemcached(); $Redis = AppCore::getRedis(); $Registry = Registry::getInstance(); if (!filter_var($id, FILTER_VALIDATE_INT)) { $cachekey = sprintf("railpage:images.camera.id=%s", $id); if (!($lookup = $Memcached->fetch($cachekey))) { $lookup = $Database->fetchOne("SELECT id FROM image_camera WHERE url_slug = ?", $id); if ($lookup) { $Memcached->save($cachekey, $lookup); } } if (!filter_var($lookup, FILTER_VALIDATE_INT)) { throw new Exception("Could not find a camera ID from URL slug " . $id); } $id = $lookup; } $regkey = sprintf(Camera::CACHE_KEY, $id); try { $Camera = $Registry->get($regkey); } catch (Exception $e) { if (!($Camera = $Redis->fetch($regkey))) { $Camera = new Camera($id); $Redis->save($regkey, $Camera, strtotime("+1 day")); } $Registry->set($regkey, $Camera); } return $Camera; }
/** * Constructor * @since Version 3.10.0 */ public function __construct() { $this->smarty = AppCore::GetSmarty(); $this->cacheProvider = AppCore::GetMemcached(); }
/** * Get a user ID from a given username * @since Version 3.10.0 * @param string $username * @return int */ public static function getUserId($username) { $dataBase = (new AppCore())->getDatabaseConnection(); $cacheHandler = AppCore::GetMemcached(); $user_id = false; $mckey = sprintf("railpage:username=%s;user_id", $username); if (!($user_id = $cacheHandler->fetch($mckey))) { $query = "SELECT user_id FROM nuke_users WHERE username = ? LIMIT 0, 1"; $user_id = $dataBase->fetchOne($query, $username); $cacheHandler->save($mckey, $user_id, strtotime("+1 year")); } return $user_id; }
/** * Convert a Google Maps link into embedded content * @since Version 3.10.0 * @param \DOMElement $e * @return \DOMElement */ public static function EmbedGoogleMap(DOMElement $e) { $timer = Debug::GetTimer(); $Config = AppCore::GetConfig(); $lookup = pq($e)->attr("href"); // Prevent this from f*****g with links in the middle of sentences if (pq($e)->text() != $lookup) { return $e; } if (!preg_match("#google.com(.au)?/maps/(.*)\\@([0-9\\-.]{8,13}),([0-9\\-.]{8,13})#", $lookup, $matches[0]) && !preg_match("#goo.gl/maps/([a-zA-Z0-9]{10,13})#", $lookup, $matches[1])) { return $e; } $basehtml = '<iframe width="%s" height="%s" frameborder="0" style="border:0" src="https://www.google.com/maps/embed/v1/view?key=%s &zoom=%d&maptype=%s¢er=%s" allowfullscreen> </iframe>'; $params = ["100%", 600, $Config->Google->API_Key, 15, "satellite"]; foreach ($matches as $val) { if (!count($val)) { continue; } // Co-ordinates known, great if (count($val) === 5) { $params[] = $val[3] . "," . $val[4]; continue; } // Co-ordinates not known. Shit. Better look 'em up if (count($val) !== 2) { continue; } $Memcached = AppCore::GetMemcached(); $cachekey = sprintf("google:url.shortner=%s", $val[1]); if (!($return = $Memcached->fetch($cachekey))) { $GuzzleClient = new Client(); $url = sprintf("https://www.googleapis.com/urlshortener/v1/url?shortUrl=%s&key=%s", $lookup, "AIzaSyC1lUe1h-gwmFqj9xDTDYI9HYVTUxNscCA"); $response = $GuzzleClient->get($url); // F****d it if ($response->getStatusCode() != 200) { return $e; } $return = json_decode($response->getBody(), true); $Memcached->save($cachekey, $return); } // Get out if it looks problematic if ($return['status'] != "OK") { return $e; } pq($e)->attr("href", $return['longUrl'])->text($return['longUrl']); return self::EmbedGoogleMap($e); continue; } pq($e)->replaceWith(vsprintf($basehtml, $params)); Debug::LogEvent(__METHOD__, $timer); return $e; }
/** * Commit changes to this camera * @since Version 3.10.0 * @return \Railpage\Images\Camera */ public function commit() { $this->validate(); $data = ["make" => $this->manufacturer, "model" => $this->name, "url_slug" => $this->slug, "image" => $this->image, "text" => $this->text, "meta" => json_encode($this->meta)]; if (filter_var($this->id, FILTER_VALIDATE_INT)) { $where = ["id = ?" => $this->id]; $this->db->update("image_camera", $data, $where); } if (!filter_var($this->id, FILTER_VALIDATE_INT)) { $this->db->insert("image_camera", $data); $this->id = $this->db->lastInsertId(); } /** * Flush cache */ $regkey = sprintf(self::CACHE_KEY, $this->id); $Redis = AppCore::GetRedis(); $Memcached = AppCore::GetMemcached(); $Redis->delete($regkey); $Memcached->delete($regkey); $this->makeURLs(); return $this; }
/** * Render the page * @since Version 3.10.0 * @return string */ public function render() { if (!$this->userObject instanceof User) { throw new InvalidArgumentException("No valid user object has been provided"); } #$this->smarty->clearCache($this->template, $this->unique); if ($this->smarty->isCached($this->template, $this->unique)) { Debug::LogCLI("!! Template file " . $this->template . " is already cached for unique ID " . $this->unique); return $this->smarty->fetch($this->template, $this->unique); } Debug::LogCLI("Template file " . $this->template . " is NOT cached for unique ID \"" . $this->unique . "\""); /** * Get user alerts */ if (!$this->userObject->guest) { global $acl; $alerts = $this->userObject->getAlerts($acl); $this->smarty->Assign("alerts", $alerts, true); } /** * Get the latest jobs */ $newjobs = array(); foreach ((new Jobs())->yieldNewJobs(5) as $Job) { $newjobs[] = $Job->getArray(); } $this->smarty->Assign("jobs", $newjobs, true); /** * Upcoming events */ $Memcached = AppCore::GetMemcached(); $cachekey = "railpage.home.upcomingevents"; $upcoming = []; if (!($upcoming = $Memcached->fetch($cachekey))) { $Events = new Events(); $upcoming = []; foreach ($Events->getUpcomingEvents(5) as $row) { //$Event = EventsFactory::CreateEvent($row['event_id']); $EventDate = new EventDate($row['id']); $data = $EventDate->getArray(); $upcoming[] = $data; } $Memcached->save("railpage.home.upcomingevents", $upcoming, strtotime("+5 minutes")); } $this->smarty->Assign("upcomingevents", $upcoming); /** * New photos */ $this->smarty->Assign("newphotos", RecentImages::getNewest(5)); /** * Chronicle */ $Chronicle = new Chronicle(); $this->smarty->Assign("chronicle", $Chronicle->getEntriesForToday(10)); /** * Get the latest railcam photo */ $Camera = new Camera(1); $Photo = $Camera->getLatest(false); $railcam = $Photo->getArray(); $railcam['sizes']['small']['source'] = ImageCache::cache($railcam['sizes']['small']['source']); $this->smarty->Assign("railcam", $railcam); $this->smarty->Assign("railcam_updated", ContentUtility::relativeTime($railcam['dates']['taken'])); /** * First check if this user has a personalised news feed */ if (filter_var($this->userObject->id, FILTER_VALIDATE_INT) && $this->userObject->id > 0) { $Feed = new Feed(); $Feed->setUser($this->userObject)->getFilters(); if (count($Feed->filter_words) || count($Feed->filter_topics)) { $latest = $Feed->findArticles(0, 20); foreach ($latest as $id => $article) { $article['sid'] = $article['story_id']; $article['catid'] = $article['topic_id']; $article['hometext'] = preg_replace("@(\\[b\\]|\\[\\/b\\])@", "", $article['story_blurb']); $article['informant'] = $article['username']; $article['informant_id'] = $article['user_id']; $article['ForumThreadId'] = $article['forum_topic_id']; $article['topictext'] = $article['topic_title']; $article['topic'] = $article['topic_id']; $article['featured_image'] = $article['story_image']; $article['title'] = $article['story_title']; $article['time_relative'] = time2str($article['story_time_unix']); $latest[$id] = $article; } } } $this->smarty->Assign("personalfeed", isset($latest)); /** * No personal news feed - go ahead as normal */ if (!isset($latest)) { /** * Instantiate the base News module */ $News = new Base(); /** * Get the latest 15 news articles */ $latest = $News->latest(20); } /** * Format titles and tags for the latest news articles */ foreach ($latest as $id => $data) { /** * Load the JSON for this article */ if (!isset($data['sid'])) { $data['sid'] = $data['story_id']; } $json = json_decode(News::getArticleJSON($data['sid']), true); $latest[$id]['hometext'] = isset($json['article']['blub']) ? wpautop(process_bbcode($json['article']['blub'])) : wpautop(process_bbcode($json['article']['blurb'])); $latest[$id]['hometext'] = strip_tags($latest[$id]['hometext'], "<a><p><img><br><br /><strong><em>"); $latest[$id]['title'] = format_topictitle($data['title']); $latest[$id]['topic'] = $json['article']['topic']; $latest[$id]['topic_highlight'] = ColourUtility::String2Hex($latest[$id]['topic_title']); $latest[$id]['url'] = $json['article']['url']; $latest[$id]['author'] = $json['article']['author']; $latest[$id]['staff'] = $json['article']['staff']; if (!empty($latest[$id]['featured_image'])) { $latest[$id]['featured_image'] = ImageCache::cache($latest[$id]['featured_image']); } // Get the first paragraph from the home text preg_match("/<p>(.*)<\\/p>/", $latest[$id]['hometext'], $matches); $latest[$id]['hometext'] = strip_tags($matches[1]); if (empty($json['article']['body']) && !empty($json['article']['source'])) { $latest[$id]['url'] = $json['article']['source']; } /** * Pre-rendering */ $this->smarty->addHeadTag(sprintf("<link rel='prerender' href='%s'>", $json['article']['url']['url'])); } /** * Slice the first news article off */ $newsLatest = array_shift($latest); /** * Send them to Smarty */ $this->smarty->assign("newsLatest", $newsLatest); $this->smarty->assign("news", $latest); $this->smarty->assign("pagecontrols", '<p style="background: #333; background: rgba(0, 0, 0, 0.6);margin: -20px;padding: 10px;margin-top: 20px; text-align: center;">Wasting time and bandwidth since 1992</p>'); if ($this->params['handheld']) { $this->smarty->assign("pagecontrols", '<p style="background: #333; background: rgba(0, 0, 0, 0.6);margin: 0px -20px;padding: 0px;margin-top: 40px; text-align: center;font-size:1em;">Wasting time and bandwidth since 1992</p>'); } return $this->smarty->fetch($this->template, $this->unique); }
/** * Get path of this route * @since Version 3.9 * @return array */ public function getPath() { $mckey = sprintf("railpage:gtfs.path;provider=%s;route=%s", $this->Provider->getProviderName(), $this->short_name); $Memcached = AppCore::GetMemcached(); if ($path = $Memcached->fetch($mckey)) { return $path; } $query = sprintf("SELECT id, service_id, trip_id, trip_headsign, shape_id, meta FROM %s_trips WHERE route_id = '%s' LIMIT 1", $this->Provider->getDbPrefix(), $this->short_name); $result = $this->adapter->query($query, Adapter::QUERY_MODE_EXECUTE); if ($result) { foreach ($result as $row) { $trip = $row->getArrayCopy(); } } if (!isset($trip)) { return false; } $query = "SELECT t.id, t.arrival_time, t.departure_time, t.stop_id, t.stop_sequence, t.pickup_type, t.drop_off_type, t.timepoint, t.meta AS time_meta, \r\n s.stop_code, s.stop_name, s.stop_lat, s.stop_lon, s.location_type, s.wheelchair_boarding, s.platform_code, s.meta AS stop_meta\r\n FROM %s_stop_times AS t LEFT JOIN %s_stops AS s ON t.stop_id = s.stop_id\r\n WHERE t.trip_id = %d ORDER BY t.stop_sequence"; $query = sprintf($query, $this->Provider->getDbPrefix(), $this->Provider->getDbPrefix(), $trip['trip_id']); $result = $this->adapter->query($query, Adapter::QUERY_MODE_EXECUTE); $path = array(); if ($result) { foreach ($result as $row) { $url = new Url(sprintf("/timetables?mode=stop&provider=%s&id=%d", $this->Provider->getProviderName(), $row['stop_id'])); $row = $row->getArrayCopy(); $row['stop_meta'] = json_decode($row['stop_meta'], true); $row['time_meta'] = json_decode($row['time_meta'], true); $row['url'] = $url->getURLs(); $path[] = $row; } } $Memcached->save($mckey, $path, strtotime("+1 month")); return $path; }
/** * Get OpenGraph tags from a specified URL * Really stupid and elaborate Memcached expiry handling is due to a bug in Debian's PHP5-Memcached package * * @since Version 3.10.0 * @param string $url * @return array */ public static function GetOpenGraphTags($url) { $Memcached = AppCore::GetMemcached(); $mckey = md5($url); if ($result = $Memcached->fetch($mckey)) { $exp = $Memcached->fetch(sprintf("%s-exp", $mckey)); if ($exp < time()) { $Memcached->delete($mckey); $Memcached->delete(sprintf("%s-exp", $mckey)); $result = false; } } if (!$result) { /** * Ensure our OG handler is loaded */ require_once "vendor" . DS . "scottmac" . DS . "opengraph" . DS . "OpenGraph.php"; $graph = \OpenGraph::fetch($url); $result = array(); foreach ($graph as $key => $value) { $result[$key] = $value; } $Memcached->save($mckey, $result, 0); // 0 or will not cache $Memcached->save(sprintf("%s-exp", $mckey), strtotime("+1 day"), 0); // alternate method of specifying expiry } return $result; }
/** * Fetch an image from either Memcached or its source URL * @since Version 3.10.0 * @param string $url * @return string */ private function getImageString($url) { $Cache = AppCore::GetMemcached(); $cachekey = sprintf("image=%s;v2", md5($url)); Debug::LogCLI("Looking for image in cache provider"); Debug::LogCLI($cachekey); if ($string = $Cache->fetch($cachekey)) { Debug::LogCLI("Image found in cache"); return $string; } Debug::LogCLI("Image " . $url . " not found in cache. Fetching and storing..."); set_time_limit(20); $string = file_get_contents($url); $Cache->save($cachekey, $string, 0); return $string; }