railpage/railpagecore

View on GitHub
lib/Users/Timeline.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

/**
 * Utility class for user functions
 * @since Version 3.9.1
 * @package Railpage
 * @author Michael Greenhill
 */

namespace Railpage\Users;

use stdClass;
use Exception;
use DateTime;
use DateTimeZone;
use Railpage\Debug;
use Railpage\AppCore;
use Railpage\BanControl\BanControl;
use Railpage\Module;
use Railpage\Url;
use Railpage\Forums\Thread;
use Railpage\Forums\Forum;
use Railpage\Forums\Forums;
use Railpage\Forums\Index;
use Railpage\ContentUtility;

use Railpage\Users\Timeline\Utility\Grammar;

class Timeline extends AppCore {
    
    /**
     * 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;
        
    }
    
    /**
     * Get an SQL query used to exclude forums from timeline lookup
     * @since Version 3.9.1
     * @param \Railpage\Users\User $User
     * @return string
     */
    
    private function getFilteredForums() {
        
        $timer = Debug::GetTimer(); 
        
        if (!isset($this->User->Guest) || !$this->User->Guest instanceof User) {
            return "";
        }
        
        $mckey = sprintf("forum.post.filter.user:%d", $this->User->Guest->id);
        
        if ($forum_post_filter = $this->Redis->fetch($mckey)) {
            return $forum_post_filter;
        }
        
        $Forums = new Forums;
        $Index = new Index;
        
        $acl = $Forums->setUser($this->User->Guest)->getACL();
        
        $allowed_forums = array(); 
        
        foreach ($Index->forums() as $row) {
            $Forum = new Forum($row['forum_id']);
            
            if ($Forum->setUser($this->User->Guest)->isAllowed(Forums::AUTH_READ)) {
                $allowed_forums[] = $Forum->id;
            }
        }
        
        $forum_filter = "AND p.forum_id IN (" . implode(",", $allowed_forums) . ")";
        
        if (count($allowed_forums) === 0) {
            $this->Redis->save($mckey, "", strtotime("+1 week"));
            return "";
        }
        
        $forum_post_filter = "AND id NOT IN (SELECT l.id AS log_id
            FROM log_general AS l 
            LEFT JOIN nuke_bbposts AS p ON p.post_id = l.value
            WHERE l.key = 'post_id' 
            " . $forum_filter . ")";
        
        $this->Redis->save($mckey, $forum_post_filter, strtotime("+1 week"));
        
        Debug::LogEvent(__METHOD__, $timer);
        
        return $forum_post_filter;
    }
    
    /**
     * Format the timeline data, one row at a time, for grammatical purposes
     * @since Version 3.9.1
     * @param array $row
     * @return array
     */
    
    private function processGrammar($row) {
        
        $row['event']['action'] = ""; 
        $row['event']['article'] = ""; 
        $row['event']['object'] = ""; 
        $row['event']['preposition'] = ""; 
        
        $row['title'] = str_ireplace(array("loco link created"), array("linked a locomotive"), $row['title']);
        
        $row = $this->processGrammarAction($row);
        $row = $this->processGrammarPreposition($row); 
        $row = $this->processGrammarArticle($row); 
        
        return $row;
    }
    
    /**
     * Process and format the action (removed/suggested/etc) of a timeline item
     * @since Version 3.9.1
     * @param array $row
     * @return array
     */
    
    private function processGrammarAction($row) {
        
        $timer = Debug::GetTimer(); 
        
        $row['event']['action'] = Grammar::getAction($row);
        $row['event']['object'] = Grammar::getObject($row);
        
        if ($row['title'] == "Loco link removed") {
            $row['event']['action'] = "removed";
            $row['event']['object'] = "linked locomotive";
            $row['event']['article'] = "a";
            $row['event']['preposition'] = "from";
        }
        
        Debug::LogEvent(__METHOD__, $timer);
        
        return $row;
        
    }
    
    /**
     * Process and format the preposition (to/from/of) of a timeline item
     * @since Version 3.9.1
     * @param array $row
     * @return array
     */
    
    private function processGrammarPreposition($row) {
        
        $timer = Debug::GetTimer(); 
        
        $row['event']['preposition'] = Grammar::getPrepositionTo($row);
        $row['event']['preposition'] = Grammar::getPrepositionFrom($row);
        $row['event']['preposition'] = Grammar::getPrepositionOf($row);
        $row['event']['preposition'] = Grammar::getPrepositionIn($row);
        
        Debug::LogEvent(__METHOD__, $timer);
        
        return $row;
        
    }
    
    /**
     * Process and format the article (the/a/an) of a timeline item
     * @since Version 3.9.1
     * @param array $row
     * @return array
     */
    
    private function processGrammarArticle($row) {
        
        $timer = Debug::GetTimer(); 
        
        $row['event']['article'] = Grammar::getArticle_OfIn($row); 
        $row['event']['article'] = Grammar::getArticle_AnA($row);
        
        
        if (preg_match("@(date)@Di", $row['event']['object'], $matches) && preg_match("@(edited)@Di", $row['event']['action'], $matches)) {
            $row['event']['preposition'] = "for";
        }
        
        $row = Grammar::getArticle_The($row); 
        
        
        Debug::LogEvent(__METHOD__, $timer);
        
        return $row;
    }
}