echo "<a href='" . URL("conversation/markAsRead/" . $conversation["conversationId"] . "?token=" . ET::$session->token . "&return=" . urlencode(ET::$controller->selfURL)) . "' class='unreadIndicator' title='" . T("Mark as read") . "'>" . $conversation["unread"] . "</a> "; } // Output controls which apply to this conversation. echo "<span class='controls'>"; // A Jump to last/unread link, depending on the user and the unread state. if (ET::$session->user and $conversation["unread"]) { echo "<a href='" . URL($conversationURL . "/unread") . "' class='jumpToUnread'>" . T("Jump to unread") . "</a>"; } else { echo "<a href='" . URL($conversationURL . "/last") . "' class='jumpToLast'>" . T("Jump to last") . "</a>"; } // If we're highlighting search terms (i.e. if we did a fulltext search), then output a "show matching posts" link. if (ET::$session->get("highlight")) { echo " <a href='" . URL($conversationURL . "/?search=" . urlencode($data["fulltextString"])) . "' class='showMatchingPosts'>" . T("Show matching posts") . "</a>"; } echo "</span>"; ?> </div> <div class='col-channel'><?php $channel = $data["channelInfo"][$conversation["channelId"]]; echo "<a href='" . URL(searchURL("", $channel["slug"])) . "' class='channel channel-{$conversation["channelId"]}' data-channel='{$channel["slug"]}'>{$channel["title"]}</a>"; ?> </div> <div class='col-lastPost'><?php echo "<span class='action'>" . avatar($conversation["lastPostMemberId"], $conversation["lastPostMemberAvatarFormat"], "thumb"), " ", sprintf(T("%s posted %s"), "<span class='lastPostMember name'>" . memberLink($conversation["lastPostMemberId"], $conversation["lastPostMember"]) . "</span>", "<span class='lastPostTime'>" . relativeTime($conversation["lastPostTime"], true) . "</span>"), "</span>"; ?> </div> <div class='col-replies'><?php echo "<span>" . Ts("%s reply", "%s replies", $conversation["replies"]) . "</span>"; ?> </div> </li>
if (empty($member["preferences"]["hideOnline"])) { $lastAction = ET::memberModel()->getLastActionInfo($member["lastActionTime"], $member["lastActionDetail"]); if ($lastAction) { echo "<" . (!empty($lastAction[1]) ? "a href='{$lastAction[1]}'" : "span") . " class='online' title='" . T("Online") . ($lastAction[0] ? " (" . sanitizeHTML($lastAction[0]) . ")" : "") . "'><i class='icon-circle'></i></" . (!empty($lastAction[1]) ? "a" : "span") . ">"; } } ?> <span class='group subText'><?php echo memberGroup($member["account"], $member["groups"]); ?> </span> </div> <div class='col-lastActive'> <span class='subText'><?php printf(T("Last active %s"), empty($member["preferences"]["hideOnline"]) ? "<span title='" . date(T("date.full"), $member["lastActionTime"]) . "'>" . relativeTime($member["lastActionTime"], true) . "</span>" : "[" . T("hidden") . "]"); ?> </span> </div> <div class='col-replies'> <span class='subText'><?php echo Ts("%s post", "%s posts", $member["countPosts"]); ?> </span> </div> </li>
/** * Common initialization for all controllers, called on every page load. This will add basic user links to * the "user" menu, and add core JS files and language definitions. * * If this is overridden, parent::init() should be called to maintain consistency between controllers. * * @return void */ public function init() { // Check for updates to the esoTalk software, but only if we're the root admin and we haven't checked in // a while. if (ET::$session->userId == C("esoTalk.rootAdmin") and C("esoTalk.admin.lastUpdateCheckTime") + C("esoTalk.updateCheckInterval") < time()) { ET::upgradeModel()->checkForUpdates(); } if ($this->responseType === RESPONSE_TYPE_DEFAULT) { // If the user IS NOT logged in, add the 'login' and 'sign up' links to the bar. if (!ET::$session->user) { $this->addToMenu("user", "join", "<a href='" . URL("user/join?return=" . urlencode($this->selfURL)) . "' class='link-join'>" . T("Sign Up") . "</a>"); $this->addToMenu("user", "login", "<a href='" . URL("user/login?return=" . urlencode($this->selfURL)) . "' class='link-login'>" . T("Log In") . "</a>"); } else { $this->addToMenu("user", "user", "<a href='" . URL("member/me") . "'>" . avatar(ET::$session->user, "thumb") . name(ET::$session->user["username"]) . "</a>"); $this->addToMenu("user", "settings", "<a href='" . URL("settings") . "' class='link-settings'>" . T("Settings") . "</a>"); if (ET::$session->isAdmin()) { $this->addToMenu("user", "administration", "<a href='" . URL("admin") . "' class='link-administration'>" . T("Administration") . "</a>"); } $this->addToMenu("user", "logout", "<a href='" . URL("user/logout?token=" . ET::$session->token) . "' class='link-logout'>" . T("Log Out") . "</a>"); } // Get the number of members currently online and add it as a statistic. if (C("esoTalk.members.visibleToGuests") or ET::$session->user) { $online = ET::SQL()->select("COUNT(*)")->from("member")->where("UNIX_TIMESTAMP()-:seconds<lastActionTime")->bind(":seconds", C("esoTalk.userOnlineExpire"))->exec()->result(); $stat = Ts("statistic.online", "statistic.online.plural", number_format($online)); $stat = "<a href='" . URL("members/online") . "' class='link-membersOnline'>{$stat}</a>"; $this->addToMenu("statistics", "statistic-online", $stat); } $this->addToMenu("meta", "copyright", "<a href='http://esotalk.org/' target='_blank'>" . T("Powered by") . " esoTalk</a>"); // Set up some default JavaScript files and language definitions. $this->addJSFile("core/js/lib/jquery.js", true); $this->addJSFile("core/js/lib/jquery.migrate.js", true); $this->addJSFile("core/js/lib/jquery.misc.js", true); $this->addJSFile("core/js/lib/jquery.history.js", true); $this->addJSFile("core/js/lib/jquery.scrollTo.js", true); $this->addJSFile("core/js/global.js", true); $this->addJSLanguage("message.ajaxRequestPending", "message.ajaxDisconnected", "Loading...", "Notifications"); $this->addJSVar("notificationCheckInterval", C("esoTalk.notificationCheckInterval")); } $this->trigger("init"); }
/** * Get a human-friendly string (eg. 1 hour ago) for how much time has passed since a given time. * * @param int $then UNIX timestamp of the time to work out how much time has passed since. * @param bool $precise Whether or not to return "x minutes/seconds", or just "a few minutes". * @return string A human-friendly time string. * * @package esoTalk */ function relativeTime($then, $precise = false) { // If there is no $then, we can only assume that whatever it is never happened... if (!$then) { return T("never"); } // Work out how many seconds it has been since $then. $ago = time() - $then; // If $then happened less than 1 second ago, say "Just now". if ($ago < 1) { return T("just now"); } // If this happened over a year ago, return "x years ago". if ($ago >= ($period = 60 * 60 * 24 * 365.25)) { $years = round($ago / $period); return Ts("%d year ago", "%d years ago", $years); } elseif ($ago >= ($period = 60 * 60 * 24 * (365.25 / 12)) * 2) { $months = round($ago / $period); return Ts("%d month ago", "%d months ago", $months); } elseif ($ago >= ($period = 60 * 60 * 24 * 7)) { $weeks = round($ago / $period); return Ts("%d week ago", "%d weeks ago", $weeks); } elseif ($ago >= ($period = 60 * 60 * 24)) { $days = round($ago / $period); return Ts("%d day ago", "%d days ago", $days); } elseif ($ago >= ($period = 60 * 60)) { $hours = round($ago / $period); return Ts("%d hour ago", "%d hours ago", $hours); } // If we're going for a precise value, go on to test at the minute/second level. if ($precise) { // If this happened over a minute ago, return "x minutes ago". if ($ago >= ($period = 60)) { $minutes = round($ago / $period); return Ts("%d minute ago", "%d minutes ago", $minutes); } elseif ($ago >= 1) { return Ts("%d second ago", "%d seconds ago", $ago); } } // Otherwise, just return "Just now". return T("just now"); }
public function handler_conversationController_renderScrubberBefore($sender, $data) { echo "<div class='conversationViews'><i class='icon-eye-open'></i> " . Ts("%s view", "%s views", $data["conversation"]["views"]) . "</div>"; }
/** * Display a list of conversations, optionally filtered by channel(s) and a search string. * * @return void */ public function action_index($channelSlug = false) { if (!$this->allowed()) { return; } list($channelInfo, $currentChannels, $channelIds, $includeDescendants) = $this->getSelectedChannels($channelSlug); // Now we need to construct some arrays to determine which channel "tabs" to show in the view. // $channels is a list of channels with the same parent as the current selected channel(s). // $path is a breadcrumb trail to the depth of the currently selected channel(s). $channels = array(); $path = array(); // Work out what channel we will use as the "parent" channel. This will be the last item in $path, // and its children will be in $channels. $curChannel = false; // If channels have been selected, use the first of them. if (count($currentChannels)) { $curChannel = $channelInfo[$currentChannels[0]]; } // If the currently selected channel has no children, or if we're not including descendants, use // its parent as the parent channel. if ($curChannel and $curChannel["lft"] >= $curChannel["rgt"] - 1 or !$includeDescendants) { $curChannel = @$channelInfo[$curChannel["parentId"]]; } // If no channel is selected, make a faux parent channel. if (!$curChannel) { $curChannel = array("lft" => 0, "rgt" => PHP_INT_MAX, "depth" => -1); } // Now, finally, go through all the channels and add ancestors of the "parent" channel to the $path, // and direct children to the list of $channels. Make sure we don't include any channels which // the user has unsubscribed to. foreach ($channelInfo as $channel) { if ($channel["lft"] > $curChannel["lft"] and $channel["rgt"] < $curChannel["rgt"] and $channel["depth"] == $curChannel["depth"] + 1 and empty($channel["unsubscribed"])) { $channels[] = $channel; } elseif ($channel["lft"] <= $curChannel["lft"] and $channel["rgt"] >= $curChannel["rgt"]) { $path[] = $channel; } } // Store the currently selected channel in the session, so that it can be automatically selected // if "New conversation" is clicked. if (!empty($currentChannels)) { ET::$session->store("searchChannelId", $currentChannels[0]); } // Get the search string request value. $searchString = R("search"); // Last, but definitely not least... perform the search! $search = ET::searchModel(); $conversationIDs = $search->getConversationIDs($channelIds, $searchString, count($currentChannels) or !ET::$session->userId); // If this page was originally accessed at conversations/markAsRead/all?search=whatever (the // markAsRead method simply calls the index method), then mark the results as read. if ($this->controllerMethod == "markasread" and ET::$session->userId) { ET::conversationModel()->markAsRead($conversationIDs, ET::$session->userId); } $results = $search->getResults($conversationIDs); // Were there any errors? Show them as messages. if ($search->errorCount()) { $this->messages($search->errors(), "warning dismissable"); } else { $this->highlight($search->fulltext); } // Pass on a bunch of data to the view. $this->data("results", $results); $this->data("limit", $search->limit); $this->data("showViewMoreLink", $search->areMoreResults()); $this->data("channelPath", $path); $this->data("channelTabs", $channels); $this->data("currentChannels", $currentChannels); $this->data("channelInfo", $channelInfo); $this->data("channelSlug", $channelSlug = $channelSlug ? $channelSlug : "all"); $this->data("searchString", $searchString); $this->data("fulltextString", implode(" ", $search->fulltext)); // Construct a canonical URL and add to the breadcrumb stack. $slugs = array(); foreach ($currentChannels as $channel) { $slugs[] = $channelInfo[$channel]["slug"]; } $url = "conversations/" . urlencode(($k = implode(" ", $slugs)) ? $k : "all") . ($searchString ? "?search=" . urlencode($searchString) : ""); $this->pushNavigation("conversations", "search", URL($url)); $this->canonicalURL = URL($url, true); // If we're loading the page in full... if ($this->responseType === RESPONSE_TYPE_DEFAULT) { // Update the user's last action. ET::memberModel()->updateLastAction("search"); // Add a link to the RSS feed in the bar. // $this->addToMenu("meta", "feed", "<a href='".URL(str_replace("conversations/", "conversations/index.atom/", $url))."' id='feed'>".T("Feed")."</a>"); $controls = ETFactory::make("menu"); // Mark as read controls if (ET::$session->user) { $controls->add("markAllAsRead", "<a href='" . URL("conversations/markAllAsRead/?token=" . ET::$session->token . "' id='control-markAllAsRead'><i class='icon-check'></i> " . T("Mark all as read") . "</a>")); $controls->add("markListedAsRead", "<a href='" . URL("conversations/{$channelSlug}/?search=" . urlencode($searchString) . "&markAsRead=1&token=" . ET::$session->token . "' id='control-markListedAsRead'><i class='icon-list'></i> " . T("Mark listed as read") . "</a>")); } // Add the default gambits to the gambit cloud: gambit text => css class to apply. $gambits = array("main" => array(T("gambit.sticky") => array("gambit-sticky", "icon-pushpin")), "time" => array(T("gambit.order by newest") => array("gambit-orderByNewest", "icon-list-ol"), T("gambit.active last ? hours") => array("gambit-activeLastHours", "icon-time"), T("gambit.active last ? days") => array("gambit-activeLastDays", "icon-calendar"), T("gambit.active today") => array("gambit-activeToday", "icon-asterisk"), T("gambit.dead") => array("gambit-dead", "icon-remove"), T("gambit.locked") => array("gambit-locked", "icon-lock")), "member" => array(T("gambit.author:") . T("gambit.member") => array("gambit-author", "icon-user"), T("gambit.contributor:") . T("gambit.member") => array("gambit-contributor", "icon-user")), "replies" => array(T("gambit.has replies") => array("gambit-hasReplies", "icon-comment"), T("gambit.has >10 replies") => array("gambit-replies", "icon-comments"), T("gambit.order by replies") => array("gambit-orderByReplies", "icon-list-ol")), "text" => array(T("gambit.title:") . " ?" => array("gambit-title", "icon-font")), "misc" => array(T("gambit.random") => array("gambit-random", "icon-random"), T("gambit.reverse") => array("gambit-reverse", "icon-exchange"))); // Add some more personal gambits if there is a user logged in. if (ET::$session->user) { addToArrayString($gambits["main"], T("gambit.private"), array("gambit-private", "icon-envelope-alt"), 1); addToArrayString($gambits["main"], T("gambit.starred"), array("gambit-starred", "icon-star"), 2); addToArrayString($gambits["main"], T("gambit.draft"), array("gambit-draft", "icon-pencil"), 3); addToArrayString($gambits["main"], T("gambit.ignored"), array("gambit-ignored", "icon-eye-close"), 4); addToArrayString($gambits["time"], T("gambit.unread"), array("gambit-unread", "icon-inbox"), 0); addToArrayString($gambits["member"], T("gambit.author:") . T("gambit.myself"), array("gambit-authorMyself", "icon-smile"), 0); addToArrayString($gambits["member"], T("gambit.contributor:") . T("gambit.myself"), array("gambit-contributorMyself", "icon-smile"), 2); } $this->trigger("constructGambitsMenu", array(&$gambits)); // Construct the gambits menu based on the above arrays. $gambitsMenu = ETFactory::make("menu"); $linkPrefix = "conversations/" . $channelSlug . "/?search=" . urlencode(!empty($searchString) ? $searchString . " + " : ""); foreach ($gambits as $section => $items) { foreach ($items as $gambit => $classes) { $gambitsMenu->add($classes[0], "<a href='" . URL($linkPrefix . urlencode("#" . $gambit)) . "' class='{$classes[0]}' data-gambit='{$gambit}'>" . (!empty($classes[1]) ? "<i class='{$classes[1]}'></i> " : "") . "{$gambit}</a>"); } end($gambits); if ($section !== key($gambits)) { $gambitsMenu->separator(); } } $this->data("controlsMenu", $controls); $this->data("gambitsMenu", $gambitsMenu); // Construct a list of keywords to use in the meta tags. $keywords = array(); foreach ($channelInfo as $c) { if ($c["depth"] == 0) { $keywords[] = strtolower($c["title"]); } } // Add meta tags to the header. $this->addToHead("<meta name='keywords' content='" . sanitizeHTML(($k = C("esoTalk.meta.keywords")) ? $k : implode(",", $keywords)) . "'>"); $lastKeyword = reset(array_splice($keywords, count($keywords) - 1, 1)); $this->addToHead("<meta name='description' content='" . sanitizeHTML(($d = C("esoTalk.meta.description")) ? $d : sprintf(T("forumDescription"), C("esoTalk.forumTitle"), implode(", ", $keywords), $lastKeyword)) . "'>"); // If this is not technically the homepage (if it's a search page) the we don't want it to be indexed. if ($searchString) { $this->addToHead("<meta name='robots' content='noindex, noarchive'>"); } // Add JavaScript language definitions and variables. $this->addJSLanguage("Starred", "Unstarred", "gambit.member", "gambit.more results", "Filter conversations", "Jump to last"); $this->addJSVar("searchUpdateInterval", C("esoTalk.search.updateInterval")); $this->addJSVar("currentSearch", $searchString); $this->addJSVar("currentChannels", $currentChannels); $this->addJSFile("core/js/lib/jquery.cookie.js"); $this->addJSFile("core/js/autocomplete.js"); $this->addJSFile("core/js/search.js"); // Add an array of channels in the form slug => id for the JavaScript to use. $channels = array(); foreach ($channelInfo as $id => $c) { $channels[$id] = $c["slug"]; } $this->addJSVar("channels", $channels); // Get a bunch of statistics... $queries = array("post" => ET::SQL()->select("COUNT(*)")->from("post")->get(), "conversation" => ET::SQL()->select("COUNT(*)")->from("conversation")->get(), "member" => ET::SQL()->select("COUNT(*)")->from("member")->get()); $sql = ET::SQL(); foreach ($queries as $k => $query) { $sql->select("({$query}) AS {$k}"); } $stats = $sql->exec()->firstRow(); // ...and show them in the footer. foreach ($stats as $k => $v) { $stat = Ts("statistic.{$k}", "statistic.{$k}.plural", number_format($v)); if ($k == "member" and (C("esoTalk.members.visibleToGuests") or ET::$session->user)) { $stat = "<a href='" . URL("members") . "'>{$stat}</a>"; } $this->addToMenu("statistics", "statistic-{$k}", $stat, array("before" => "statistic-online")); } $this->render("conversations/index"); } elseif ($this->responseType === RESPONSE_TYPE_VIEW) { $this->render("conversations/results"); } elseif ($this->responseType === RESPONSE_TYPE_AJAX) { $this->json("channels", $this->getViewContents("channels/tabs", $this->data)); $this->render("conversations/results"); } elseif ($this->responseType === RESPONSE_TYPE_JSON) { $this->json("results", $results); $this->render(); } }
} ?> <div class='info'> <a href='<?php echo URL("conversations/" . $channel["slug"]); ?> ' class='channel channel-<?php echo $channel["channelId"]; ?> '><?php echo $channel["title"]; ?> </a> <span class='stats'><?php echo Ts("%s conversation", "%s conversations", $channel["countConversations"]); ?> </span> <?php if (!empty($channel["description"])) { ?> <p class='description'><?php echo $channel["description"]; ?> </p><?php } ?> </div> </li> <?php
/** * Common initialization for all controllers, called on every page load. This will add basic user links to * the "user" menu, and add core JS files and language definitions. * * If this is overridden, parent::init() should be called to maintain consistency between controllers. * * @return void */ public function init() { // Check for updates to the esoTalk software, but only if we're the root admin and we haven't checked in // a while. if (ET::$session->userId == C("esoTalk.rootAdmin") and C("esoTalk.admin.lastUpdateCheckTime") + C("esoTalk.updateCheckInterval") < time()) { ET::upgradeModel()->checkForUpdates(); } if ($this->responseType === RESPONSE_TYPE_DEFAULT) { // If the user IS NOT logged in, add the 'login' and 'sign up' links to the bar. if (!ET::$session->user) { $this->addToMenu("user", "login", "<a href='" . URL("user/login?return=" . urlencode($this->selfURL)) . "' class='link-login'>" . T("Log In") . "</a>"); $this->addToMenu("user", "join", "<a href='" . URL("user/join?return=" . urlencode($this->selfURL)) . "' class='link-join'>" . T("Sign Up") . "</a>"); } else { $this->addToMenu("user", "user", "<a href='" . URL("member/me") . "'>" . avatar(ET::$session->userId, ET::$session->user["avatarFormat"], "thumb") . name(ET::$session->user["username"]) . "</a>"); // Fetch all unread notifications so we have a count for the notifications button. $notifications = ET::activityModel()->getNotifications(-1); $count = count($notifications); $this->addToMenu("user", "notifications", "<a href='" . URL("settings/notifications") . "' id='notifications' class='popupButton " . ($count ? "new" : "") . "'><span>{$count}</span></a>"); // Show messages with these notifications. $this->notificationMessages($notifications); $this->addToMenu("user", "settings", "<a href='" . URL("settings") . "' class='link-settings'>" . T("Settings") . "</a>"); if (ET::$session->isAdmin()) { $this->addToMenu("user", "administration", "<a href='" . URL("admin") . "' class='link-administration'>" . T("Administration") . "</a>"); } $this->addToMenu("user", "logout", "<a href='" . URL("user/logout") . "' class='link-logout'>" . T("Log Out") . "</a>"); } // Get the number of members currently online and add it as a statistic. $online = ET::SQL()->select("COUNT(*)")->from("member")->where("UNIX_TIMESTAMP()-:seconds<lastActionTime")->bind(":seconds", C("esoTalk.userOnlineExpire"))->exec()->result(); $stat = Ts("statistic.online", "statistic.online.plural", number_format($online)); $stat = "<a href='" . URL("members/online") . "' class='link-membersOnline'>{$stat}</a>"; $this->addToMenu("statistics", "statistic-online", $stat); $this->addToMenu("meta", "copyright", "<a href='http://esotalk.com/'>Powered by esoTalk" . (ET::$session->isAdmin() ? " " . ESOTALK_VERSION : "") . "</a>"); // Set up some default JavaScript files and language definitions. $this->addJSFile("js/lib/jquery.js", true); $this->addJSFile("js/lib/jquery.misc.js", true); $this->addJSFile("js/lib/jquery.history.js", true); $this->addJSFile("js/lib/jquery.scrollTo.js", true); $this->addJSFile("js/global.js", true); $this->addJSLanguage("message.ajaxRequestPending", "message.ajaxDisconnected", "Loading...", "Notifications"); $this->addJSVar("notificationCheckInterval", C("esoTalk.notificationCheckInterval")); // If config/custom.css contains something, add it to be included in the page. if (file_exists($file = PATH_CONFIG . "/custom.css") and filesize($file) > 0) { $this->addCSSFile("config/custom.css", true); } } $this->trigger("init"); }
/** * Display a list of conversations, optionally filtered by channel(s) and a search string. * * @return void */ function index($channelSlug = false) { // Add the default gambits to the gambit cloud: gambit text => css class to apply. $gambits = array(T("gambit.active last ? hours") => "gambit-activeLastHours", T("gambit.active last ? days") => "gambit-activeLastDays", T("gambit.active today") => "gambit-activeToday", T("gambit.author:") . T("gambit.member") => "gambit-author", T("gambit.contributor:") . T("gambit.member") => "gambit-contributor", T("gambit.dead") => "gambit-dead", T("gambit.has replies") => "gambit-hasReplies", T("gambit.has >10 replies") => "gambit-replies", T("gambit.locked") => "gambit-locked", T("gambit.more results") => "gambit-more", T("gambit.order by newest") => "gambit-orderByNewest", T("gambit.order by replies") => "gambit-orderByReplies", T("gambit.random") => "gambit-random", T("gambit.reverse") => "gambit-reverse", T("gambit.sticky") => "gambit-sticky"); // Add some more personal gambits if there is a user logged in. if (ET::$session->user) { $gambits += array(T("gambit.contributor:") . T("gambit.myself") => "gambit-contributorMyself", T("gambit.author:") . T("gambit.myself") => "gambit-authorMyself", T("gambit.draft") => "gambit-draft", T("gambit.muted") => "gambit-muted", T("gambit.private") => "gambit-private", T("gambit.starred") => "gambit-starred", T("gambit.unread") => "gambit-unread"); } list($channelInfo, $currentChannels, $channelIds, $includeDescendants) = $this->getSelectedChannels($channelSlug); // Now we need to construct some arrays to determine which channel "tabs" to show in the view. // $channels is a list of channels with the same parent as the current selected channel(s). // $path is a breadcrumb trail to the depth of the currently selected channel(s). $channels = array(); $path = array(); // Work out what channel we will use as the "parent" channel. This will be the last item in $path, // and its children will be in $channels. $curChannel = false; // If channels have been selected, use the first of them. if (count($currentChannels)) { $curChannel = $channelInfo[$currentChannels[0]]; } // If the currently selected channel has no children, or if we're not including descendants, use // its parent as the parent channel. if ($curChannel and $curChannel["lft"] >= $curChannel["rgt"] - 1 or !$includeDescendants) { $curChannel = @$channelInfo[$curChannel["parentId"]]; } // If no channel is selected, make a faux parent channel. if (!$curChannel) { $curChannel = array("lft" => 0, "rgt" => PHP_INT_MAX, "depth" => -1); } // Now, finally, go through all the channels and add ancestors of the "parent" channel to the $path, // and direct children to the list of $channels. Make sure we don't include any channels which // the user has unsubscribed to. foreach ($channelInfo as $channel) { if ($channel["lft"] > $curChannel["lft"] and $channel["rgt"] < $curChannel["rgt"] and $channel["depth"] == $curChannel["depth"] + 1 and empty($channel["unsubscribed"])) { $channels[] = $channel; } elseif ($channel["lft"] <= $curChannel["lft"] and $channel["rgt"] >= $curChannel["rgt"]) { $path[] = $channel; } } // Store the currently selected channel in the session, so that it can be automatically selected // if "New conversation" is clicked. if (!empty($currentChannels)) { ET::$session->store("channelId", $currentChannels[0]); } // Get the search string request value. $searchString = R("search"); // Last, but definitely not least... perform the search! $search = ET::searchModel(); $conversationIDs = $search->getConversationIDs($channelIds, $searchString, count($currentChannels)); $results = $search->getResults($conversationIDs); // Were there any errors? Show them as messages. if ($search->errorCount()) { $this->messages($search->errors(), "warning dismissable"); } else { $words = array(); foreach ($search->fulltext as $term) { if (preg_match_all('/"(.+?)"/', $term, $matches)) { $words[] = $matches[1]; $term = preg_replace('/".+?"/', '', $term); } $words = array_unique(array_merge($words, explode(" ", $term))); } ET::$session->store("highlight", $words); } // Pass on a bunch of data to the view. $this->data("results", $results); $this->data("showViewMoreLink", $search->areMoreResults()); $this->data("channelPath", $path); $this->data("channelTabs", $channels); $this->data("currentChannels", $currentChannels); $this->data("channelInfo", $channelInfo); $this->data("channelSlug", $channelSlug ? $channelSlug : "all"); $this->data("searchString", $searchString); $this->data("fulltextString", implode(" ", $search->fulltext)); $this->data("gambits", $gambits); // If we're loading the page in full... if ($this->responseType === RESPONSE_TYPE_DEFAULT) { // Update the user's last action. ET::memberModel()->updateLastAction("search"); // Construct a canonical URL and add to the breadcrumb stack. $slugs = array(); foreach ($currentChannels as $channel) { $slugs[] = $channelInfo[$channel]["slug"]; } $url = "conversations/" . urlencode(($k = implode(" ", $slugs)) ? $k : "all") . ($searchString ? "?search=" . urlencode($searchString) : ""); $this->pushNavigation("conversations", "search", URL($url)); $this->canonicalURL = URL($url, true); // Add a link to the RSS feed in the bar. $this->addToMenu("meta", "feed", "<a href='" . URL(str_replace("conversations/", "conversations/index.atom/", $url)) . "' id='feed'>" . T("Feed") . "</a>"); // Construct a list of keywords to use in the meta tags. $keywords = array(); foreach ($channelInfo as $c) { if ($c["depth"] == 0) { $keywords[] = strtolower($c["title"]); } } // Add meta tags to the header. $this->addToHead("<meta name='keywords' content='" . sanitizeHTML(($k = C("esoTalk.meta.keywords")) ? $k : implode(",", $keywords)) . "'>"); list($lastKeyword) = array_splice($keywords, count($keywords) - 1, 1); $this->addToHead("<meta name='description' content='" . sanitizeHTML(($d = C("esoTalk.meta.description")) ? $d : sprintf(T("forumDescription"), C("esoTalk.forumTitle"), implode(", ", $keywords), $lastKeyword)) . "'>"); // If this is not technically the homepage (if it's a search page) the we don't want it to be indexed. if ($searchString) { $this->addToHead("<meta name='robots' content='noindex, noarchive'>"); } // Add JavaScript language definitions and variables. $this->addJSLanguage("Starred", "Unstarred", "gambit.member", "gambit.more results", "Filter conversations", "Jump to last"); $this->addJSVar("searchUpdateInterval", C("esoTalk.search.updateInterval")); $this->addJSVar("currentSearch", $searchString); $this->addJSVar("currentChannels", $currentChannels); $this->addJSFile("js/lib/jquery.cookie.js"); $this->addJSFile("js/autocomplete.js"); $this->addJSFile("js/search.js"); // Add an array of channels in the form slug => id for the JavaScript to use. $channels = array(); foreach ($channelInfo as $id => $c) { $channels[$id] = $c["slug"]; } $this->addJSVar("channels", $channels); // Get a bunch of statistics... $queries = array("post" => ET::SQL()->select("COUNT(*)")->from("post")->get(), "conversation" => ET::SQL()->select("COUNT(*)")->from("conversation")->get(), "member" => ET::SQL()->select("COUNT(*)")->from("member")->get()); $sql = ET::SQL(); foreach ($queries as $k => $query) { $sql->select("({$query}) AS {$k}"); } $stats = $sql->exec()->firstRow(); // ...and show them in the footer. foreach ($stats as $k => $v) { $stat = Ts("statistic.{$k}", "statistic.{$k}.plural", number_format($v)); if ($k == "member") { $stat = "<a href='" . URL("members") . "'>{$stat}</a>"; } $this->addToMenu("statistics", "statistic-{$k}", $stat, array("before" => "statistic-online")); } $this->render("conversations/index"); } elseif ($this->responseType === RESPONSE_TYPE_VIEW) { $this->render("conversations/results"); } elseif ($this->responseType === RESPONSE_TYPE_AJAX) { $this->json("channels", $this->getViewContents("channels/tabs", $this->data)); $this->render("conversations/results"); } elseif ($this->responseType === RESPONSE_TYPE_JSON) { $this->json("results", $results); $this->render(); } }
// If this conversation is stickied, output an excerpt from its first post. if ($conversation["sticky"]) { echo "<div class='excerpt'>" . ET::formatter()->init($conversation["firstPost"])->inline(true)->firstLine()->clip(200)->format()->get() . "</div>"; } ?> </div> <div class='col-channel'><?php $channel = $data["channelInfo"][$conversation["channelId"]]; echo "<a href='" . URL(searchURL("", $channel["slug"])) . "' class='channel channel-{$conversation["channelId"]}' data-channel='{$channel["slug"]}'>{$channel["title"]}</a>"; ?> </div> <div class='col-replies'> <i class='icon-comment<?php if (!$conversation["replies"]) { echo "-alt"; } ?> '></i> <?php echo "<span>" . Ts("%s reply", "%s replies", $conversation["replies"]) . "</span>"; // Output an "unread indicator", showing the number of unread posts. if (ET::$session->user and $conversation["unread"]) { echo " <a href='" . URL("conversation/markAsRead/" . $conversation["conversationId"] . "?token=" . ET::$session->token . "&return=" . urlencode(ET::$controller->selfURL)) . "' class='unreadIndicator' title='" . T("Mark as read") . "'>" . Ts("%s new", "%s new", $conversation["unread"]) . "</a> "; } ?> </div> <div class='col-lastPost'><?php echo "<span class='action'>" . avatar(array("memberId" => $conversation["lastPostMemberId"], "avatarFormat" => $conversation["lastPostMemberAvatarFormat"], "email" => $conversation["lastPostMemberEmail"]), "thumb"), " ", sprintf(T("%s posted %s"), "<span class='lastPostMember name'>" . memberLink($conversation["lastPostMemberId"], $conversation["lastPostMember"]) . "</span>", "<a href='" . URL($conversationURL . "/unread") . "' class='lastPostTime'>" . relativeTime($conversation["lastPostTime"], true) . "</a>"), "</span>"; ?> </div> </li>