/** * 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; }
/** * @depends test_createPhotos * @depends test_createUser */ public function test_votePhotos($photoComp, $userObject) { $photoComp->SubmissionsDateOpen = (new DateTime())->sub(new DateInterval("P10D")); $photoComp->SubmissionsDateClose = (new DateTime())->sub(new DateInterval("P3D")); $photoComp->VotingDateOpen = (new DateTime())->sub(new DateInterval("P1D")); $photoComp->VotingDateClose = (new DateTime())->add(new DateInterval("P10D")); $this->assertTrue($photoComp->canUserVote($userObject)); $this->assertFalse(empty($photoComp->getPhotosAsArray(true))); foreach ($photoComp->getPhotos() as $Photo) { Debug::LogCLI("Voting for photo ID " . $Photo->Image->id); $this->assertEquals(0, $photoComp->getNumVotesForImage($Photo->Image)); $this->assertTrue($photoComp->canUserVote($userObject, $Photo->Image)); $photoComp->submitVote($userObject, $Photo->Image); $this->assertfalse($photoComp->canUserVote($userObject, $Photo->Image)); $this->assertEquals(1, $photoComp->getNumVotesForImage($Photo->Image)); $this->winning_id = $Photo->Image->id; break; } $photoComp->getNumVotesForUser(new User()); $photoComp->SubmissionsDateOpen->sub(new DateInterval("P10W")); $photoComp->SubmissionsDateClose->sub(new DateInterval("P10W")); $photoComp->VotingDateOpen->sub(new DateInterval("P10W")); $photoComp->VotingDateClose->sub(new DateInterval("P10W")); $photoComp->getVoteCountsPerDay(); $winner = $photoComp->getWinningPhoto(); $this->assertFalse($winner == false); $this->assertEquals($this->winning_id, $winner->Image->id); }
/** * Get latest photo from this railcam * @since Version 3.10.0 * @return \Railpage\Railcams\Photo * @param boolean $update Update cached data if it's stale */ public function getLatest($update = true) { if ($footage = $this->getLatestFootage("image")) { return ["id" => $footage['id'], "title" => "", "description" => "", "dates" => ["taken" => $footage['datestored']], "sizes" => ["original" => ["source" => $footage['url']['original']]]]; } $mckey = sprintf("railpage:railcam=%d;latest=1", $this->id); /** * Shitty, hacky way to handle Memcached expiry bug on Debian */ $mckey_age = $mckey . ";expiry"; $exp = $this->Memcached->fetch($mckey_age); if ($update && (!$exp || $exp < time())) { $this->Memcached->delete($mckey); } /** * Fetch from Memcached, or load from API */ if (!($latest = $this->Memcached->fetch($mckey))) { $latest = $this->getPhotos(1); Debug::LogCLI("Fetched " . count($latest['photo']) . " photo(s)"); foreach ($latest['photo'] as $key => $photo) { Debug::LogCLI("Processing photo..."); $photo['timezone'] = $this->timezone; $Date = new DateTime($photo['datetaken']); $Date->setTimezone(new DateTimeZone($this->timezone)); $photo['datetaken'] = $Date->format("c"); $latest['photo'][$key] = $photo; } Debug::LogCLI("Saving photo in Memcached"); $this->Memcached->save($mckey, $latest, 0); Debug::LogCLI("Saving photo expiry in Memcached"); $this->Memcached->save($mckey_age, strtotime("+5 minutes"), 0); } return $this->getPhoto($latest['photo'][0]['id']); }
/** * Create other sizes * @since Version 3.10.0 * @return void */ public static function createOtherSizes() { $sleep = 2; $sleep = false; $Database = (new AppCore())->getDatabaseConnection(); $query = "SELECT i.id,\r\n square.size AS square, square.source AS square_src, square.width AS square_w, square.height AS square_h,\r\n large_square.size AS large_square, large_square.source AS large_square_src, large_square.width AS large_square_w, large_square.height AS large_square_h,\r\n small.size AS small, small.source AS small_src, small.width AS small_w, small.height AS small_h,\r\n small_320.size AS small_320, small_320.source AS small_320_src, small_320.width AS small_320_w, small_320.height AS small_320_h,\r\n medium.size AS medium, medium.source AS medium_src, medium.width AS medium_w, medium.height AS medium_h,\r\n medium_640.size AS medium_640, medium_640.source AS medium_640_src, medium_640.width AS medium_640_w, medium_640.height AS medium_640_h,\r\n medium_800.size AS medium_800, medium_800.source AS medium_800_src, medium_800.width AS medium_800_w, medium_800.height AS medium_800_h,\r\n original.size AS original, original.source AS original_src, original.width AS original_w, original.height AS original_h\r\n FROM gallery_mig_image AS i\r\n LEFT JOIN gallery_mig_image_sizes AS square ON square.photo_id = i.id AND square.size = 'square'\r\n LEFT JOIN gallery_mig_image_sizes AS large_square ON large_square.photo_id = i.id AND large_square.size = 'large_square'\r\n LEFT JOIN gallery_mig_image_sizes AS small ON small.photo_id = i.id AND small.size = 'small'\r\n LEFT JOIN gallery_mig_image_sizes AS small_320 ON small_320.photo_id = i.id AND small_320.size = 'small_320'\r\n LEFT JOIN gallery_mig_image_sizes AS medium ON medium.photo_id = i.id AND medium.size = 'medium'\r\n LEFT JOIN gallery_mig_image_sizes AS medium_640 ON medium_640.photo_id = i.id AND medium_640.size = 'medium_640'\r\n LEFT JOIN gallery_mig_image_sizes AS medium_800 ON medium_800.photo_id = i.id AND medium_800.size = 'medium_800'\r\n LEFT JOIN gallery_mig_image_sizes AS original ON original.photo_id = i.id AND original.size = 'original'\r\n WHERE i.hidden = 0\r\n AND square.size IS NULL\r\n AND large_square.size IS NULL\r\n AND small.size IS NULL\r\n AND small_320.size IS NULL\r\n AND medium.size IS NULL\r\n AND medium_640.size IS NULL\r\n AND medium_800.size IS NULL\r\n LIMIT 0, 250"; $result = $Database->fetchAll($query); /** * Set our desired sizes */ $sizes = ["square" => ["width" => 75, "height" => 75], "large_square" => ["width" => 150, "height" => 150], "small" => ["width" => 240, "height" => 0], "small_320" => ["width" => 320, "height" => 0], "medium" => ["width" => 500, "height" => 0], "medium_640" => ["width" => 640, "height" => 0], "medium_800" => ["width" => 800, "height" => 0]]; /** * Loop through the results and start building the sizes */ foreach ($result as $row) { /** * Load the original image from disk. If it doesn't exist then continue to the next array item */ $filename = sprintf("%s%s", Album::ALBUMS_DIR, $row['original_src']); if (!file_exists($filename)) { continue; } $ext = pathinfo($filename, PATHINFO_EXTENSION); $allowedtypes = ["jpeg", "jpg", "png", "gif"]; if (!in_array($ext, $allowedtypes)) { continue; } $noext = str_replace("." . $ext, "", $filename); $image = file_get_contents($filename); Debug::LogCLI("Source image " . $filename); /** * Loop through each required size */ foreach ($sizes as $key => $dims) { /** * If the size already exists in DB then proceed to the next size */ if (!is_null($row[$key]) || $key == "original") { continue; } /** * Break out of the loop if the desired size is larger than than the original image */ if ($dims['width'] > $row['original_w']) { continue; } $dstfile = sprintf("%s.%s.%s", $noext, $key, $ext); if (file_exists($dstfile)) { unlink($dstfile); } Debug::LogCLI(" Creating " . $key . " from image " . $filename); Debug::LogCLI(""); $Image = WideImage::loadFromString($image); if ($dims['width'] == $dims['height']) { $size = $Image->resize($dims['width'], $dims['height'], "outside"); $size = $size->crop(0, "middle", $dims['width'], $dims['height']); } if ($dims['width'] != $dims['height']) { $size = $Image->resize($dims['width'], $dims['width'], "inside"); } $quality = $dims['width'] <= 240 ? 80 : 100; file_put_contents($dstfile, $size->asString("jpg", $quality)); if (file_exists($dstfile)) { Debug::LogCLI(" Image created, inserting into DB"); Debug::LogCLI(" " . $dstfile); $data = ["photo_id" => $row['id'], "size" => $key, "source" => $dstfile, "width" => $size->getWidth(), "height" => $size->getHeight()]; $Database->insert("gallery_mig_image_sizes", $data); } Debug::LogCLI(" ---"); } if ($sleep) { Debug::LogCLI("-------------------------------"); Debug::LogCLI(""); Debug::LogCLI("Sleeping for two seconds"); Debug::LogCLI(""); sleep($sleep); } Debug::LogCLI("-------------------------------"); Debug::LogCLI(""); } }
/** * Find an image by provider and provider image ID * @since Version 3.8.7 * @param string $provider * @param int $photoId * @param mixed $option * @throws \Exception if $provider is null * @throws \Exception if $photoId is null * @param int $option */ public function findImage($provider = null, $photoId = null, $option = null) { if (is_null($provider)) { throw new Exception("Cannot lookup image from image provider - no provider given (hint: Flickr, WestonLangford)"); } if (!preg_match("/([a-zA-Z0-9]+)/", $photoId) || $photoId === 0) { throw new Exception("Cannot lookup image from image provider - no provider image ID given"); } $mckey = sprintf("railpage:image;provider=%s;id=%s", $provider, $photoId); if (defined("NOREDIS") && NOREDIS == true || $option != self::OPT_REFRESH && !($id = $this->Redis->fetch($mckey))) { Debug::LogCLI("Found photo ID " . $photoId . " in database"); $id = $this->db->fetchOne("SELECT id FROM image WHERE provider = ? AND photo_id = ?", array($provider, $photoId)); $this->Redis->save($mckey, $id, strtotime("+1 month")); } if (isset($id) && filter_var($id, FILTER_VALIDATE_INT)) { return new Image($id, $option); } Debug::LogCLI("Photo ID " . $photoId . " not found in local cache"); $Image = new Image(); $Image->provider = $provider; $Image->photo_id = $photoId; $Image->populate(true, $option); return $Image; }
/** * Update the geoplace reference for this image * * @since Version 3.9.1 * @return void */ public function updateGeoPlace() { if (!filter_var($this->lat, FILTER_VALIDATE_FLOAT) || !filter_var($this->lat, FILTER_VALIDATE_FLOAT)) { return; } $timer = microtime(true); $GeoPlaceID = PlaceUtility::findGeoPlaceID($this->lat, $this->lon); #var_dump($GeoPlaceID);die; $data = ["geoplace" => $GeoPlaceID]; $where = ["id = ?" => $this->id]; $this->db->update("image", $data, $where); $this->Memcached->delete($this->mckey); $this->Redis->delete($this->mckey); Debug::logEvent(__METHOD__, $timer); Debug::LogCLI(__METHOD__, $timer); return; }
/** * Grab an image from the web and store it locally * @since Version 3.10.0 * @param string $remoteFile * @param string $localFile * @return void */ private function grab($remoteFile, $localFile) { $GuzzleClient = new GuzzleClient(); Debug::LogCLI("Fetching {$remoteFile}"); $response = $GuzzleClient->get($remoteFile); if ($response->getStatusCode() != 200 && $response->getStatusCode() != 304) { throw new Exception("Unexpected HTTP status code " . $response->getStatusCode() . " encountered when fetching " . $remoteFile); } $image = $response->getBody(); if (!file_put_contents($localFile, $image)) { throw new Exception("File was fetched from remote source, but could not save to destination file " . $localFile); } return; }
/** * Fetch the latest information on an album from the relevant provider * @since Version 3.10.0 * @param array $album * @return void */ public static function ScrapeAlbum($album) { Debug::LogCLI("Scraping album ID " . $album['album_id'] . " from provider " . $album['provider']); set_time_limit(30); $Database = AppCore::GetDatabase(); $Provider = ImageUtility::CreateImageProvider($album['provider']); // Assume Flickr for now, we can update the internal code later $params = ["photoset_id" => $album['album_id']]; $albumdata = $Provider->execute("flickr.photosets.getInfo", $params); // Insert this shit into the database $data = ["scraped" => new Zend_Db_Expr("NOW()"), "meta" => json_encode($albumdata['photoset'])]; $where = ["id = ?" => $album['id']]; $Database->update("image_scrape_album", $data, $where); // Fetch the photos $params['user_id'] = $albumdata['photoset']['owner']; $photos = $Provider->execute("flickr.photosets.getPhotos", $params); foreach ($photos['photoset']['photo'] as $photo) { Debug::LogCLI("Scraping photo ID " . $photo['id']); set_time_limit(10); ImageFactory::CreateImage($photo['id'], $album['provider']); Debug::LogCLI("Sleeping for 2 seconds..."); sleep(2); } }
/** * Queue the newsletter for dispatch * @since Version 3.10.0 * @return \Railpage\Newsletters\Weekly */ private function queue() { $this->Notification->subject = "[News] " . $this->Newsletter->subject; $this->Notification->body = $this->html; $this->Notification->meta['decoration'] = $this->replacements; $this->Notification->addHeader("List-Unsubscribe", "<http://railpage.com.au/unsubscribe?email=##email_encoded##&newsletter=weekly>"); Debug::LogCLI("Queueing notification for dispatch"); $this->Notification->commit(); $this->Newsletter->status = Newsletter::STATUS_SENT; Debug::LogCLI("Commiting the newsletter to the database"); $this->Newsletter->commit(); /** * Update the last sent timestamp */ foreach ($this->user_ids as $user_id) { $query = "INSERT INTO nuke_users_flags (user_id, newsletter_weekly_last) VALUES(" . $user_id . ", NOW()) ON DUPLICATE KEY UPDATE newsletter_weekly_last = NOW()"; $ZendDB->query($query); } 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); }
/** * Fetch EXIF data for the queued images * @since Version 3.10.0 * @return void */ public static function scrapeExifQueue() { $sleep = 10; $break = 50; $Database = (new AppCore())->getDatabaseConnection(); $query = "SELECT f.image_id FROM image_flags AS f LEFT JOIN image AS i ON f.image_id = i.id WHERE f.exifqueue = 1 AND i.provider IS NOT NULL ORDER BY f.image_id DESC"; $exif = new Exif(); $ids = []; foreach ($Database->fetchAll($query) as $row) { $imageObject = new Image($row['image_id']); $exif->getImageExif($imageObject); $ids[] = $imageObject->id; if (count($ids) == $break) { Debug::LogCLI("Updating " . $break . " records"); $query = "UPDATE image_flags SET exifqueue = 0 WHERE image_id IN (" . implode(",", $ids) . ")"; $Database->query($query); $ids = []; break; sleep($sleep); } } Debug::LogCLI("Mark all queued images as scraped"); $query = "UPDATE image_flags SET exifqueue = 0 WHERE exifqueue = 1"; $Database->query($query); return; }
/** * Assemble the collage * @since Version 3.10.0 * @return \Railpage\Images\Collage */ private function assemble() { Debug::LogCLI("Assembling the collage"); $imageSize = $this->findThumbnailSize(); $size = $imageSize['size']; $this->canvas = imagecreatetruecolor($this->width, $this->height); $counter_x = 0; $counter_y = 0; $offset_x = 0; $offset_y = 0; //$images_per_row = ceil($this->width / $size['thumbWidth']); foreach ($this->Images as $Image) { Debug::LogCLI("Running Image ID " . $Image->id); // Fetch the image $raw = $this->getImageString($Image->sizes[$size]['source']); $thumb = imagecreatefromstring($raw); // Place the thubmnail onto our canvas imagecopyresampled($this->canvas, $thumb, $offset_x, $offset_y, 0, 0, $imageSize['thumbWidth'], $imageSize['thumbHeight'], $imageSize['thumbWidth'], $imageSize['thumbHeight']); if (php_sapi_name() == "cli" && !defined("PHPUNIT_RAILPAGE_TESTSUITE")) { var_dump($offset_x); var_dump($offset_y); var_dump($offset_x + $imageSize['thumbWidth']); var_dump($offset_y + $imageSize['thumbHeight']); var_dump($imageSize['thumbWidth']); var_dump($imageSize['thumbHeight']); } $offset_x += $imageSize['thumbWidth']; $counter_x++; Debug::LogCLI("counter_x: " . $counter_x); Debug::LogCLI("offset_x: " . $offset_x); if ($offset_x >= $this->width) { Debug::LogCLI("Wrapping line"); $counter_x = 0; $counter_y++; $offset_y += $imageSize['thumbHeight']; $offset_x = 0; } } }