/** * Assign UTM parameters to an individual URL * @since Version 3.10.0 * @param \Railpage\Newsletters\Newsletter $Newsletter * @param string $url * @return string */ public static function CreateUTMParametersForLink(Newsletter $Newsletter, $url) { if (is_array($url)) { $url = $url['url']; } $utm = ["utm_source=newsletter", "utm_medium=email", sprintf("utm_campaign=%s", ContentUtility::generateUrlSlug($Newsletter->subject))]; $parts = parse_url($url); if (!isset($parts['query'])) { $url .= "?" . implode("&", $utm); return $url; } $parts['query'] = array_merge(explode("&", $parts['query']), $utm); return ContentUtility::unparse_url($parts); }
public function test_relativeTime() { $this->assertEquals("4 seconds ago", ContentUtility::relativeTime(new DateTime("4 seconds ago"), new DateTime())); $this->assertEquals("one minute ago", ContentUtility::relativeTime(strtotime("1 minutes ago"))); $this->assertEquals("4 minutes ago", ContentUtility::relativeTime(strtotime("4 minutes ago"))); $this->assertEquals("4 hours ago", ContentUtility::relativeTime(strtotime("4 hours ago"))); $this->assertEquals("yesterday", ContentUtility::relativeTime(strtotime("24 hours ago"))); $this->assertEquals("2 days ago", ContentUtility::relativeTime(strtotime("2 days ago"))); $this->assertEquals("one week ago", ContentUtility::relativeTime(strtotime("7 days ago"))); $this->assertEquals("2 weeks ago", ContentUtility::relativeTime(strtotime("14 days ago"))); $this->assertEquals("4 weeks ago", ContentUtility::relativeTime(strtotime("1 month ago"))); $this->assertEquals("2 months ago", ContentUtility::relativeTime(strtotime("2 months ago"))); $this->assertEquals("last year", ContentUtility::relativeTime(strtotime("12 months ago"))); $this->assertEquals("10 years ago", ContentUtility::relativeTime(strtotime("10 years ago"))); $this->assertEquals("3 years ago", ContentUtility::relativeTime(strtotime("5 years ago"), new DateTime("2 years ago"))); }
/** * Add an asset * @since Version 3.9.1 * @param string $namespace * @param int $id * @param array $data * @return void */ public static function addAsset($namespace, $id, $data) { if (!is_array($data)) { throw new Exception("Cannot add asset - \$data must be an array"); return false; } $Database = (new AppCore())->getDatabaseConnection(); $data = array_merge($data, array("date" => new Zend_Db_Expr("NOW()"), "namespace" => $namespace, "namespace_key" => $id)); $meta = json_encode($data['meta']); /** * Handle UTF8 errors */ if (!$meta && json_last_error() === JSON_ERROR_UTF8) { $data['meta'] = ContentUtility::FixJSONEncode_UTF8($data['meta']); } else { $data['meta'] = $meta; } $Database->insert("asset", $data); return true; }
/** * Validate this event * @since Version 3.8.7 * @return boolean * @throws Exception if $this->title is empty * @throws Exception if $this->desc is empty * @throws Exception if $this->Category is not an instance of Railpage\Events\EventCategory */ private function validate() { if (empty($this->title)) { throw new Exception("Validation failed for event. Title cannot be empty"); } if (empty($this->desc)) { throw new Exception("Validation failed for event. Description cannot be empty"); } if (!$this->Category instanceof EventCategory) { throw new Exception("Validation failed for event. Event must have a category!"); } if (!isset($this->slug) || empty($this->slug)) { $this->createSlug(); } if (!filter_var($this->status)) { $this->status = Events::STATUS_UNAPPROVED; } if (!$this->Author instanceof User) { throw new Exception("A valid user object must be set (hint: Event::setAuthor()"); } $this->title = ContentUtility::FormatTitle($this->title); return true; }
/** * Make a URL slug * @since Version 3.9.1 * @package Railpage * @author Michael Greenhill * @return void */ private function makeSlug() { if (!empty($this->slug)) { return; } $proposal = ContentUtility::generateUrlSlug($this->name, 20); $query = "SELECT COUNT(id) FROM glossary WHERE slug = ?"; $num = $this->db->fetchOne($query, $proposal); if ($num) { $proposal .= $num; } $this->slug = $proposal; if (filter_var($this->id, FILTER_VALIDATE_INT)) { $this->commit(); } return; }
/** * Validate changes * @since Version 3.10.0 * @return boolean */ private function validate() { if (empty($this->title)) { throw new Exception("Title cannot be empty"); } if (!$this->date instanceof DateTime) { $this->date = new DateTime(); } if (empty($this->hits)) { $this->hits = 0; } if (empty($this->language)) { $this->language = "english"; } if (empty($this->permalink)) { $prop = ContentUtility::generateUrlSlug($this->title); if ($rs = $this->db->fetchAll("SELECT pid FROM nuke_pages WHERE shortname = ?", $prop)) { $prop .= count($rs); } $this->permalink = $prop; } return true; }
/** * Format an avatar * @since Version 3.9.1 * @return string * @param string $userAvatar * @param int $width * @param width $height */ public static function format($userAvatar = null, $width = 100, $height = 100) { if (is_null($userAvatar)) { return false; } $cacheHandler = AppCore::getMemcached(); $timer = Debug::getTimer(); if ($userAvatar == "http://www.railpage.com.au/modules/Forums/images/avatars/https://static.railpage.com.au/image_resize") { $userAvatar = self::DEFAULT_AVATAR; } if (empty($userAvatar) || stristr($userAvatar, "blank.gif") || stristr($userAvatar, "blank.png")) { $userAvatar = self::DEFAULT_AVATAR; return $userAvatar; } $parts = parse_url($userAvatar); if (isset($parts['host']) && $parts['host'] == "static.railpage.com.au" && isset($parts['query'])) { parse_str($parts['query'], $query); if (isset($query['w']) && isset($query['h']) && isset($query['image'])) { if ($query['w'] == $width && $query['h'] == $height) { return $userAvatar; } return sprintf("http://static.railpage.com.au/image_resize.php?w=%d&h=%d&image=%s", $width, $height, $query['image']); } } if (isset($parts['host']) && $parts['host'] == "www.gravatar.com" && isset($parts['query'])) { parse_str($parts['query'], $query); $query['s'] = $width; $bits = array(); foreach ($query as $key => $val) { $bits[] = sprintf("%s=%s", $key, $val); } $userAvatar = sprintf("%s://%s%s?%s", $parts['scheme'], $parts['host'], $parts['path'], implode("&", $bits)); return self::GravatarHTTPS($userAvatar); } $mckey = sprintf("railpage.user:avatar=%s;width=%s;height=%s", $userAvatar, $width, $height); /** * Check if this shit is in Memcache first */ if ($result = $cacheHandler->fetch($mckey)) { return self::GravatarHTTPS($result); } /** * It's not in Memcached, so let's process and cache it */ parse_str(parse_url($userAvatar, PHP_URL_QUERY), $args); if (isset($args['base64_args'])) { if (!@unserialize(base64_decode($args['base64_args']))) { // Malformed string! $userAvatar = self::DEFAULT_AVATAR; } else { // Do other stuff... $base64 = unserialize(base64_decode($args['base64_args'])); } } if (preg_match("@modules/Forums/images/avatars/(http\\:\\/\\/|https\\:\\/\\/)@", $userAvatar)) { $userAvatar = self::DEFAULT_AVATAR; } if (!preg_match("@(http\\:\\/\\/|https\\:\\/\\/)@", $userAvatar)) { $userAvatar = "http://static.railpage.com.au/modules/Forums/images/avatars/" . $userAvatar; } if (!ContentUtility::url_exists($userAvatar)) { $userAvatar = self::DEFAULT_AVATAR; } if ($width && !$height) { $height = $width; } // Is this an anigif? if (substr($userAvatar, -4, 4) == ".gif") { // Fetch the dimensions $mckey = "railpage:avatar.size=" . md5($userAvatar); if ($dimensions = $cacheHandler->fetch($mckey)) { // Do nothing } else { $dimensions = @getimagesize($userAvatar); $cacheHandler->save($mckey, $dimensions); } if (isset($dimensions['mime']) && $dimensions['mime'] == "image/gif") { // Great, it's a gif if ($width && $height) { if ($dimensions[0] <= $width && $dimensions[1] <= $height) { // It fits within the width and height - return it as-is return self::GravatarHTTPS($userAvatar); } } } } // Assume that all avatars created on dev.railpage.com.au are shit and should be re-directed to static.railpage.com.au $userAvatar = str_replace("dev.railpage.com.au", "static.railpage.com.au", $userAvatar); if ($width && $height) { $args['width'] = $width; $args['height'] = $height; $args['url'] = $userAvatar; if (empty($userAvatar)) { $args['url'] = self::DEFAULT_AVATAR; } #$userAvatar = "https://static.railpage.com.au/image_resize.php?base64_args=".base64_encode(serialize($args)); $userAvatar = sprintf("https://static.railpage.com.au/image_resize.php?w=%d&h=%d&image=%s", $args['width'], $args['height'], $args['url']); if ($width == $height) { $userAvatar .= "&square=true"; } } $cacheHandler->save($mckey, $userAvatar, 0); Debug::logEvent(__METHOD__, $timer); return self::GravatarHTTPS($userAvatar); }
/** * Create a URL slug * @since Version 3.9.1 */ private function createSlug() { $proposal = ContentUtility::generateUrlSlug($this->name); $result = $this->db->fetchAll("SELECT id_cat FROM nuke_faqCategories WHERE url_slug = ?", $proposal); if (count($result)) { $proposal .= count($result); } $this->url_slug = $proposal; }
/** * Validate changes to this manufacturer * @return boolean * @throws \Exception if $this->name is empty */ public function validate() { if (empty($this->name)) { throw new Exception("Cannot validate changes to this locomotive manufacturer: manufacturer name cannot be empty"); } if (empty($this->slug)) { $proposal = ContentUtility::generateUrlSlug($this->name, 30); $query = "SELECT manufacturer_id FROM loco_manufacturer WHERE slug = ?"; $result = $this->db->fetchAll($query, $proposal); if (count($result)) { $proposal = $proposal . count($result); } $this->slug = $proposal; $this->url = new Url(sprintf("/locos/builder/%s", $this->slug)); } $this->name = ContentUtility::FormatTitle($this->name); return true; }
/** * Generate the URL slug for this help item * @since Version 3.8.6 * @return string */ private function createSlug() { $proposal = ContentUtility::generateUrlSlug($this->title); /** * Check that we haven't used this slug already */ $result = $this->db->fetchAll("SELECT id FROM nuke_faqAnswer WHERE url_slug = ?", $proposal); if (count($result)) { $proposal .= count($result); } /** * Return it */ return $proposal; }
/** * Embed Flickr content from an OpenEmbed-friendly URL * @since Version 3.10.0 * @param \DOMElement $e * @param string $group * @return \DOMElement */ private static function drawFlickrFromOpenGraph(DOMElement $e, $group = null) { $og = ContentUtility::GetOpenGraphTags(pq($e)->attr("href")); $style = ["background-image: url(\"" . $og['image'] . "\")"]; $titlePrepend = ["flickr_photos:set" => "Photo album", "flickr_photos:photo" => "Photo"]; $titlePrepend = isset($titlePrepend[$og['type']]) ? $titlePrepend[$og['type']] . ": " : ""; if (empty($og['title'])) { $og['title'] = "Untitled"; } $og['title'] = ContentUtility::FormatTitle($og['title']); $mediaBlock = pq("<div />"); $mediaBlock->addClass("content-image")->addClass("content-flickr")->addClass("media"); $mediaBlock->attr("style", implode(";", $style)); $mediaBlock->html("<div class='media--content'><h1><a href='" . $og['url'] . "'>" . $titlePrepend . $og['title'] . "</a></h1><div class='media--lead'><a href='" . $og['url'] . "'>" . $og['description'] . "</a></div></div>"); $mediaBlockWrapper = pq("<div />"); $mediaBlockWrapper->addClass("content-image-wrapper"); //$mediaBlockWrapper->attr('style', "height: 0;padding-bottom: 56.25%;position: relative;margin-bottom:1.4em;"); $mediaBlockWrapper->html($mediaBlock); if (pq($e)->hasClass("embed-group")) { $mediaBlockWrapper->addClass("embed-group-member"); } pq($e)->replaceWith($mediaBlockWrapper); return $e; }
/** * Validate changes to this camera * @since Version 3.10.0 * @throws \Exception if $this->name is empty * @throws \Exception if $this->manufacturer is empty * @return boolean */ private function validate() { if (empty($this->name)) { throw new Exception("Camera name is empty"); } if (empty($this->manufacturer)) { throw new Exception("Manufacturer name is empty"); } if (empty($this->slug)) { $this->slug = ContentUtility::generateUrlSlug(sprintf("%s %s", $this->manufacturer, $this->name)); try { $count = $this->db->fetchAll("SELECT id FROM image_camera WHERE url_slug = ?", $this->slug); if (count($count)) { $this->slug .= count($count); } } catch (Exception $e) { // Don't care } } return true; }
/** * 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); }
/** * Generate the URL slug * @since Version 3.7.5 * @param int $id * @param string $name * @return string */ public function createSlug($id = false, $name = false) { $timer = Debug::GetTimer(); if (filter_var($id, FILTER_VALIDATE_INT) && !$name) { $name = $this->db->fetchOne("SELECT organisation_name FROM organisation WHERE organisation_id = ?", $id); } elseif (filter_var($id, FILTER_VALIDATE_INT) && is_string($name)) { // Do nothing } elseif (isset($this->name) && !empty($this->name)) { $name = $this->name; $id = $this->id; } else { return false; } $proposal = ContentUtility::generateUrlSlug($name, 200); /** * Check that we haven't used this slug already */ $result = $this->db->fetchAll("SELECT organisation_id FROM organisation WHERE organisation_slug = ? AND organisation_id != ?", array($proposal, $id)); if (count($result)) { $proposal .= count($result); } if (isset($this->slug) || empty($this->slug)) { $this->slug = $proposal; } /** * Add this slug to the database */ $data = array("organisation_slug" => $proposal); $where = array("organisation_id = ?" => $id); $rs = $this->db->update("organisation", $data, $where); Debug::LogEvent(__METHOD__, $timer); /** * Return it */ return $proposal; }
/** * Make a URL slug for a camera from brand and model * @since Version 3.10.0 * @param string $make * @param string $model * @return string */ public static function makeCameraUrlSlug($make, $model) { $prop = ContentUtility::generateUrlSlug(sprintf("%s %s", $make, $model), 30); return $prop; }
/** * Validate changes to this album * @since Version 3.10.0 * @return boolean * @throws \Exception if $this->name is empty * @throws \Exception if $this->Author is empty */ private function validate() { if (empty($this->name)) { throw new Exception("Album name is empty"); } if (empty($this->slug)) { $this->slug = ContentUtility::generateUrlSlug($this->name, 30); $query = "SELECT id FROM gallery_mig_album WHERE name = ?"; $rs = $this->db->fetchAll($query, $this->slug); if (count($rs)) { $this->slug .= count($rs); } } if (!$this->Owner instanceof User) { $this->Owner = $this->getOwner(); } if (!$this->Owner instanceof User) { throw new Exception("No valid album owner has been set"); } return true; }
/** * Create a URL slug * @since Version 3.7.5 * @return void */ private function createSlug() { $proposal = ContentUtility::generateUrlSlug($this->name); $result = $this->db->fetchAll("SELECT id FROM location WHERE slug = ?", $proposal); if (count($result)) { $proposal .= count($result); } $this->slug = $proposal; }
/** * Create a URL slug * @since Version 3.9.1 */ private function createSlug() { $proposal = ContentUtility::generateUrlSlug($this->width_metric); $result = $this->db->fetchAll("SELECT gauge_id FROM loco_gauge WHERE slug = ?", $proposal); if (count($result)) { $proposal .= count($result); } $this->slug = $proposal; return $this->slug; }
/** * Validate changes to this locotype * @since Version 3.8.7 * @return true * @throws \Exception if $this->arrangement is empty */ public function validate() { if (empty($this->name)) { throw new Exception("Cannot validate changes to this loco type: name cannot be empty"); return false; } if (empty($this->slug)) { $proposal = ContentUtility::generateUrlSlug($this->name, 30); $query = "SELECT id FROM loco_type WHERE slug = ?"; $result = $this->db->fetchAll($query, $proposal); if (count($result)) { $proposal = $proposal . count($result); } $this->slug = $proposal; $this->url = new Url(sprintf("%s/type/%s", $this->Module->url, $this->slug)); } return true; }
/** * Get and personalise the content for this newsletter * @since Version 3.10.0 * @return \Railpage\Newsletters\Weekly */ private function personaliseContent() { $replacements = array(); Debug::LogCLI("Looping through " . count($this->recipients) . " users and preparing email decoration"); $this->user_ids = array(); $counter = 0; /** * Loop through our list of users and start to curate the contents */ foreach ($this->recipients as $row) { // Flag this user ID so that we can update the "last sent" timestamp later $user_ids[] = $row['user_id']; // Sanity check : validate the email address first if (!filter_var($row['user_email'], FILTER_VALIDATE_EMAIL)) { Debug::LogCLI("Skipping user ID " . $row['user_id'] . " - \"" . $row['user_email'] . "\" is not a valid email address"); continue; } // Add the recipient $this->Notification->addRecipient($row['user_id'], $row['username'], $row['user_email']); // Assign some decoration $replacements[$row['user_email']] = array("##username##" => $row['username'], "##email##" => $row['user_email'], "##email_encoded##" => urlencode($row['user_email']), "##unsubscribe##" => sprintf("http://railpage.com.au/unsubscribe?email=%s&newsletter=weekly", urlencode($row['user_email']))); /** * Get the custom news feed articles */ Debug::LogCLI("Preparing personalised news for user ID " . $row['user_id']); // Try and create the user object. If it bombs out, we need to know about it but let the newsletter continue try { $User = UserFactory::CreateUser($row['user_id']); } catch (Exception $e) { Debug::LogCLI("Skipped user due to exception: " . $e->getMessage()); continue; } // Create the custom news feed object $Feed = new Feed(); $Feed->setUser($User); $articles = $Feed->addFilter(Feed::FILTER_UNREAD)->addFilter(Feed::FILTER_LAST_30_DAYS)->findArticles(0, 10, "story_hits"); // If the number of personalised articles is less than ten, drop the filter and simply find ten recent and unread articles if (count($articles) < 10) { Debug::LogCLI("Found " . count($articles) . " articles for user ID " . $User->id . " - dropping keyword and topic filter from feed"); $Feed->filter_words = null; $Feed->filter_topics = null; $articles = $Feed->findArticles(0, 10, "story_hits"); } // If we have less than six articles skip this user altogether. if (count($articles) < 6) { Debug::LogCLI("Found " . count($articles) . " articles for user ID " . $User->id . " - skipping"); continue; } Debug::LogCLI("Proceeding with newsletter for user ID " . $User->id); // Loop through each article and normalise the content foreach ($articles 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'] = ContentUtility::FormatTitle($article['topic_title']); $article['topic'] = $article['topic_id']; $article['featured_image'] = ImageCache::cache($article['story_image']); $article['title'] = $article['story_title']; $article['url'] = NewsletterUtility::CreateUTMParametersForLink($this->Newsletter, $article['url']); $articles[$id] = $article; } $articles = array_values($articles); if (!isset($start)) { $start = 0; } // Loop through the prepended content and assign it to the blocks foreach ($this->prependedContent as $i => $block) { $tmp = ["##block" . $i . ".subtitle##" => $block['title'], "##block" . $i . ".featuredimage##" => $block['featuredimage'], "##block" . $i . ".text##" => strip_tags(wpautop(process_bbcode($block['text'])), "<br><br /><p>"), "##block" . $i . ".link##" => strpos($block['url'], "http") === false ? "http://www.railpage.com.au" . $block['url'] : $block['url'], "##block" . $i . ".alt_title##" => $block['subtitle'], "##block" . $i . ".link_text##" => isset($block['link_text']) && !empty($block['link_text']) ? $block['link_text'] : "Continue reading"]; $replacements[$row['user_email']] = array_merge($replacements[$row['user_email']], $tmp); } // Loop through our content and assign to content blocks for ($i = count($this->prependedContent) + $start; $i < $start + $this->num_items; $i++) { $Date = new DateTime($articles[$i]['story_time']); $tmp = ["##block" . $i . ".subtitle##" => $articles[$i]['story_title'], "##block" . $i . ".featuredimage##" => $articles[$i]['story_image'], "##block" . $i . ".text##" => strip_tags(wpautop(process_bbcode($articles[$i]['story_lead'])), "<br><br /><p>"), "##block" . $i . ".link##" => strpos($articles[$i]['url'], "http") === false ? "http://www.railpage.com.au" . $articles[$i]['url'] : $articles[$i]['url'], "##block" . $i . ".alt_title##" => sprintf("Published %s", $Date->format("F j, Y, g:i a")), "##block" . $i . ".link_text##" => "Continue reading"]; $replacements[$row['user_email']] = array_merge($replacements[$row['user_email']], $tmp); } Debug::LogCLI("Completed personalisation of newsletter for user ID " . $User->id); // Increment our personalised newsletter counter $counter++; /** * Break after 150 recipients. Don't want to be flagged as a spammer, or overload the MTA */ if ($counter == 150) { break; } } $this->replacements = $replacements; return $this; }
/** * Verify the changes before committing them * @since Version 3.2 * @return boolean */ public function validate() { if (empty($this->name)) { throw new Exception("Cannot validate Operator: the operator name cannot be empty"); } if (!filter_var($this->organisation_id, FILTER_VALIDATE_INT)) { $this->organisation_id = 0; } $this->name = ContentUtility::FormatTitle($this->name); return true; }
/** * Create a URL slug * @since Version 3.8.7 */ private function createSlug() { if (!empty($this->slug)) { return; } $proposal = ContentUtility::generateUrlSlug($this->name, 14); $result = $this->db->fetchAll("SELECT id FROM event_categories WHERE slug = ?", $proposal); if (count($result)) { $proposal .= count($result); } $this->slug = $proposal; }
/** * Get this sighting as an associative array * @since Version 3.10.0 * @return array */ public function getArray() { $array = array("id" => $this->id, "lat" => $this->lat, "lon" => $this->lon, "text" => $this->text, "author" => array("id" => $this->user_id, "username" => $this->username), "timezone" => $this->timezone, "date" => array("added" => array("absolute" => $this->date_added->format("Y-m-d H:i:s"), "relative" => ContentUtility::relativeTime($this->date_added)), "seen" => array("absolute" => $this->date->format("Y-m-d H:i:s"), "relative" => ContentUtility::relativeTime($this->date))), "loco_ids" => $this->loco_ids, "meta" => $this->meta, "url" => $this->url->getURLs(), "place" => $this->Place->getArray()); return $array; }
/** * Fetch stories from Sphinx * @since Version 3.9.1 * @return array * @param int $page * @param int $limit * @param boolean $total */ private function fetchStoriesFromSphinx($page = 0, $limit = 25, $total = true) { $Sphinx = $this->getSphinx(); $query = $Sphinx->select("*")->from("idx_news_article")->orderBy("story_time_unix", "DESC")->limit($page * $limit, $limit)->where("topic_id", "=", $this->id)->where("story_active", "=", 1)->option("max_matches", 10000); $matches = $query->execute(); $meta = $Sphinx->query("SHOW META"); $meta = $meta->execute(); if (is_array($matches) && count($matches)) { $return = array("total" => $meta[1]['Value'], "children" => array(), "page" => $page, "perpage" => $limit, "topic_id" => $this->id); foreach ($matches as $id => $row) { $row['time_relative'] = ContentUtility::relativeTime($row['story_time_unix']); $row['time'] = $row['story_time']; $row['title'] = ContentUtility::FormatTitle($row['story_title']); // Match the first sentence $line = explode("\n", !empty($row['story_lead']) ? $row['story_lead'] : $row['story_blurb']); $row['firstline'] = preg_replace('/([^?!.]*.).*/', '\\1', strip_tags($line[0])); if (empty($row['story_slug'])) { $row['slug'] = $this->createSlug($row['story_id']); } $row['url'] = $this->makePermaLink($row['story_slug']); $row['hometext'] = $row['story_blurb']; $row['bodytext'] = $row['story_body']; $row['featured_image'] = $row['story_image']; $row['informant'] = $row['username']; $return['children'][$id] = $row; } return $return; } return false; }
/** * 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; }
/** * Suggest a theme to add * @since Version 3.9.1 * @return \Railpage\Images\Competitions * @param string $theme The short descriptive text for the theme (eg "At night", "Close up", etc) * @param boolean $winner True/false flag indicating if this theme has been suggested by a competition winner */ public function suggestTheme($theme, $winner = null) { if (!$this->Author instanceof User) { throw new Exception("You have not set the author of this theme (hint: Competitions::setAuthor()"); } if (empty($theme)) { throw new Exception("You haven't entered any text..."); } $theme = ContentUtility::FormatTitle($theme); $themes = $this->getSuggestedThemes(); array_unshift($themes, ["user" => ["id" => $this->Author->id, "username" => $this->Author->username], "theme" => $theme, "winner" => $winner]); $Config = new Config(); $Config->set("image.competition.suggestedthemes", json_encode($themes), "Photo competition themes"); return $this; }
/** * Commit changes to this locomotive * @since Version 3.9.1 * @return \Railpage\Locos\Date */ public function commit() { $this->validate(); $data = array("loco_unit_id" => $this->Loco->id, "loco_date_id" => $this->action_id, "date" => $this->Date->getTimestamp(), "date_end" => $this->DateEnd instanceof DateTime ? $this->DateEnd->format("Y-m-d") : NULL, "timestamp" => $this->Date->format("Y-m-d"), "text" => $this->text, "meta" => json_encode($this->meta)); if (filter_var($this->id)) { $this->Redis->delete($this->mckey); $where = array("date_id = ?" => $this->id); $this->db->update("loco_unit_date", $data, $where); } else { $this->db->insert("loco_unit_date", $data); $this->id = $this->db->lastInsertId(); } if (isset($this->Loco->meta['construction_cost'])) { $this->Loco->meta['construction_cost_inflated'] = ContentUtility::convertCurrency($this->Loco->meta['construction_cost'], Utility\LocomotiveUtility::getConstructionDate($this->Loco)); $this->Loco->commit(); } return $this; }
/** * Generate the JSON data string * * @return $this */ public function getJSON() { if (isset($this->author)) { $author = clone $this->author; if (isset($author->User) && $author->User instanceof User) { $author->User = $author->User->getArray(); } } $data = array("id" => $this->id, "title" => $this->title, "description" => $this->description, "score" => $this->getScore(), "provider" => array("name" => $this->provider, "photo_id" => $this->photo_id), "sizes" => Images::NormaliseSizes($this->sizes), "srcset" => implode(", ", Utility\ImageUtility::generateSrcSet($this)), "author" => isset($author) ? $author : false, "url" => $this->url instanceof Url ? $this->url->getURLs() : array(), "dates" => array()); $times = ["posted", "taken"]; #printArray($this->meta['dates']);die; foreach ($times as $time) { if (isset($this->meta['dates'][$time])) { $Date = filter_var($this->meta['dates'][$time], FILTER_VALIDATE_INT) ? new DateTime("@" . $this->meta['dates'][$time]) : new DateTime($this->meta['dates'][$time]); $data['dates'][$time] = array("absolute" => $Date->format("Y-m-d H:i:s"), "nice" => $Date->Format("d H:i:s") == "01 00:00:00" ? $Date->Format("F Y") : $Date->format("F j, Y, g:i a"), "relative" => ContentUtility::RelativeTime($Date)); } } if ($this->Place instanceof Place) { $data['place'] = array("url" => $this->Place->url, "lat" => $this->Place->lat, "lon" => $this->Place->lon, "name" => $this->Place->name, "country" => array("code" => $this->Place->Country->code, "name" => $this->Place->Country->name, "url" => $this->Place->Country->url)); } $this->json = json_encode($data); return $this; }
/** * Return additional head tags in a concatenated string * @since Version 3.8 * @return string */ public function getHeadTags() { $tags = array(); if (count($this->rp_meta_tags)) { foreach ($this->rp_meta_tags as $property => $content) { $tag = '<meta property="' . $property . '" content="' . htmlentities(ContentUtility::formatText($content, ["strip_formatting" => true])) . '">'; if ($property == "og:image" || $property == "twitter:image") { $tag = str_replace("&", "&", $tag); } $tags[] = $tag; } } if (count($this->head_links)) { foreach ($this->head_links as $rel => $href) { $tag = '<link rel="' . $rel . '" href="' . htmlentities($href) . '">'; $tags[] = $tag; } } if (count($this->preload['prefetch'])) { foreach ($this->preload['prefetch'] as $href) { $tag = '<link rel="prefetch" href="' . htmlentities($href) . '">'; $tags[] = $tag; } } if (count($this->preload['prerender'])) { foreach ($this->preload['prerender'] as $href) { $tag = '<link rel="prerender" href="' . htmlentities($href) . '">'; $tags[] = $tag; } } return implode("\n\t", array_merge($tags, $this->head_tags)); }
/** * Extract a user's timeline * @since Version 3.9.1 * @param \DateTime|int $dateFrom * @param \DateTime|int $dateTo * @return array */ public function GenerateTimeline($dateFrom, $dateTo) { $page = false; $items_per_page = false; if (!$this->User instanceof User) { throw new InvalidArgumentException("No user object has been provided (hint: " . __CLASS__ . "::setUser(\$User))"); } if (filter_var($dateFrom, FILTER_VALIDATE_INT)) { $page = $dateFrom; } if (filter_var($dateTo, FILTER_VALIDATE_INT)) { $items_per_page = $dateTo; } /** * Filter out forums this user doesn't have access to */ $forum_post_filter = $this->getFilteredForums(); if ($page && $items_per_page) { $query = "SELECT SQL_CALC_FOUND_ROWS * FROM log_general WHERE user_id = ? " . $forum_post_filter . " ORDER BY timestamp DESC LIMIT ?, ?"; $offset = ($page - 1) * $items_per_page; $params = array($this->User->id, $offset, $items_per_page); } if (!$page || !$items_per_page) { $query = "SELECT SQL_CALC_FOUND_ROWS * FROM log_general WHERE user_id = ? " . $forum_post_filter . " AND timestamp >= ? AND timestamp <= ? ORDER BY timestamp DESC"; $params = array($this->User->id, $dateFrom->format("Y-m-d H:i:s"), $dateTo->format("Y-m-d H:i:s")); } $timeline = array("total" => 0); if ($result = $this->db->fetchAll($query, $params)) { if ($page && $items_per_page) { $timeline['page'] = $page; $timeline['perpage'] = $items_per_page; } if (!$page || !$items_per_page) { $timeline['start'] = $dateFrom->format("Y-m-d H:i:s"); $timeline['end'] = $dateTo->format("Y-m-d H:i:s"); } $timeline['total'] = $this->db->fetchOne("SELECT FOUND_ROWS() AS total"); foreach ($result as $row) { $row['args'] = json_decode($row['args'], true); $row['timestamp'] = new DateTime($row['timestamp']); $timeline['timeline'][$row['id']] = $row; } } /** * Process the timeline data */ if (!isset($timeline['timeline'])) { return $timeline; } foreach ($timeline['timeline'] as $key => $row) { // Set their timezone $row['timestamp']->setTimezone(new DateTimeZone($this->User->timezone)); if (stristr($row['title'], "loco") && empty($row['module'])) { $row['module'] = "locos"; } /** * Check if the meta data array exists */ if (!isset($row['meta'])) { $row['meta'] = array("id" => NULL, "namespace" => NULL); } /** * Format our data for grammatical and sentence structural purposes */ $row = $this->processGrammar($row); /** * Alter the object if needed */ $row = Timeline\Utility\General::formatObject($row); /** * Set the module namespace */ $row['meta']['namespace'] = Timeline\Utility\General::getModuleNamespace($row); /** * Attempt to create a link to this object or action if none exists */ $row['meta']['url'] = Timeline\Utility\Url::createUrl($row); /** * Attempt to create a meta object title for this object or action if none exists */ $row = Timeline\Utility\ObjectTitle::generateTitle($row); /** * Compact it all together and create a succinct message */ $row['action'] = Timeline\Utility\General::compactEvents($row); /** * Create the timestamp */ $row['timestamp_nice'] = ContentUtility::relativeTime($row['timestamp']); /** * Determine the icon */ $row['glyphicon'] = Timeline\Utility\General::getIcon($row); $timeline['timeline'][$key] = $row; } return $timeline; }