/** * Add a field to the form. * * @param string $section The name of the section to add this field to. * @param string $id The name of the field. * @param mixed $renderCallback The function to call that will return the field's HTML. * @param mixed $processCallback The function to call that will process the field's input. * @param mixed $position The position to put this field relative to other fields. * @see addArrayToString * @return void */ public function addField($section, $id, $renderCallback, $processCallback, $position = false) { if (!isset($this->fields[$section])) { $this->fields[$section] = array(); } addToArrayString($this->fields[$section], $id, array("renderCallback" => $renderCallback, "processCallback" => $processCallback), $position); }
/** * Add an event handler to the "getEditControls" method of the conversation controller to add BBCode * formatting buttons to the edit controls. * * @return void */ public function handler_conversationController_getEditControls($sender, &$controls, $id) { addToArrayString($controls, "fixed", "<a href='javascript:BBCode.fixed(\"{$id}\");void(0)' title='" . T("Fixed") . "' class='bbcode-fixed'><span>" . T("Fixed") . "</span></a>", 0); addToArrayString($controls, "image", "<a href='javascript:BBCode.image(\"{$id}\");void(0)' title='" . T("Image") . "' class='bbcode-img'><span>" . T("Image") . "</span></a>", 0); addToArrayString($controls, "link", "<a href='javascript:BBCode.link(\"{$id}\");void(0)' title='" . T("Link") . "' class='bbcode-link'><span>" . T("Link") . "</span></a>", 0); addToArrayString($controls, "strike", "<a href='javascript:BBCode.strikethrough(\"{$id}\");void(0)' title='" . T("Strike") . "' class='bbcode-s'><span>" . T("Strike") . "</span></a>", 0); addToArrayString($controls, "header", "<a href='javascript:BBCode.header(\"{$id}\");void(0)' title='" . T("Header") . "' class='bbcode-h'><span>" . T("Header") . "</span></a>", 0); addToArrayString($controls, "italic", "<a href='javascript:BBCode.italic(\"{$id}\");void(0)' title='" . T("Italic") . "' class='bbcode-i'><span>" . T("Italic") . "</span></a>", 0); addToArrayString($controls, "bold", "<a href='javascript:BBCode.bold(\"{$id}\");void(0)' title='" . T("Bold") . "' class='bbcode-b'><span>" . T("Bold") . "</span></a>", 0); }
/** * Add an event handler to the "getEditControls" method of the conversation controller to add BBCode * formatting buttons to the edit controls. * * @return void */ public function handler_conversationController_getEditControls($sender, &$controls, $id) { addToArrayString($controls, "fixed", "<a href='javascript:BBCode.fixed(\"{$id}\");void(0)' title='" . T("Code") . "' class='control-fixed'><i class='icon-code'></i></a>", 0); addToArrayString($controls, "image", "<a href='javascript:BBCode.image(\"{$id}\");void(0)' title='" . T("Image") . "' class='control-img'><i class='icon-picture'></i></a>", 0); addToArrayString($controls, "link", "<a href='javascript:BBCode.link(\"{$id}\");void(0)' title='" . T("Link") . "' class='control-link'><i class='icon-link'></i></a>", 0); addToArrayString($controls, "strike", "<a href='javascript:BBCode.strikethrough(\"{$id}\");void(0)' title='" . T("Strike") . "' class='control-s'><i class='icon-strikethrough'></i></a>", 0); addToArrayString($controls, "header", "<a href='javascript:BBCode.header(\"{$id}\");void(0)' title='" . T("Header") . "' class='control-h'><i class='icon-h-sign'></i></a>", 0); addToArrayString($controls, "italic", "<a href='javascript:BBCode.italic(\"{$id}\");void(0)' title='" . T("Italic") . "' class='control-i'><i class='icon-italic'></i></a>", 0); addToArrayString($controls, "bold", "<a href='javascript:BBCode.bold(\"{$id}\");void(0)' title='" . T("Bold") . "' class='control-b'><i class='icon-bold'></i></a>", 0); }
/** * Add an event handler to the "getEditControls" method of the conversation controller to add BBCode * formatting buttons to the edit controls. * * @return void */ public function handler_conversationController_getEditControls($sender, &$controls, $id) { addToArrayString($controls, "smileys", "<a href='javascript:EmoticonAdv.showDropDown(\"{$id}\");void(0)' title='" . T("Smileys") . "' class='control-smile'><i class='icon-smile'></i></a>", 0); }
function addFieldset($fieldset, $legend, $position = false) { return addToArrayString($this->form, $fieldset, array("legend" => $legend), $position); }
public function handler_conversationsController_constructGambitsMenu($sender, &$gambits) { addToArrayString($gambits["replies"], T("gambit.order by views"), array("gambit-orderByViews", "icon-eye-open")); }
/** * 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(); } }
public function handler_membersController_constructGambitsMenu($controller, &$gambits) { $model = ET::getInstance("profileFieldModel"); $fields = $model->get(array("searchable" => 1)); $gambits["profile"] = array(); foreach ($fields as $field) { addToArrayString($gambits["profile"], mb_strtolower($field["name"], "UTF-8") . ": ?", array("gambit-profile-" . $field["fieldId"]), 1); } }
/** * Add a separator item to this menu collection. * * @param mixed $position The position to put the menu item, relative to other menu items. * @see addToArrayString * @return void */ public function separator($position = false) { addToArrayString($this->items, count($this->items) + 1, "separator", $position); }
/** * Show the member list page. * * @param string $orderBy What to sort the members by. * @param mixed $start Where to start the results from. This can be: * - An integer, in which case it will be used as a numerical offset. * - pX, where X is the "page" number. * - A letter to start from, if $orderBy is "name". * @return void */ public function action_index($orderBy = false, $start = 0) { if (!$this->allowed("esoTalk.members.visibleToGuests")) { return; } // Begin constructing a query to fetch results. $sql = ET::SQL()->from("member m"); // If we've limited results by a search string... if ($searchString = R("search")) { // Explode separate terms by the plus character. $terms = explode("+", $searchString); // Get an array of all groups which we can possibly filter by. $groups = ET::groupModel()->getAll(); $groups[GROUP_ID_ADMINISTRATOR] = array("name" => ACCOUNT_ADMINISTRATOR); $groups[GROUP_ID_MEMBER] = array("name" => ACCOUNT_MEMBER); $groups[GROUP_ID_GUEST] = array("name" => ACCOUNT_SUSPENDED); $conditions = array(); $this->trigger("parseTerms", array(&$terms, $sql, &$conditions)); foreach ($terms as $k => $term) { $term = mb_strtolower(trim($term), "UTF-8"); if (!$term) { continue; } $thisCondition = array(); // If the search string matches the start of any group names, then we'll filter members by their account/group. $group = false; foreach ($groups as $id => $g) { $name = $g["name"]; if (strpos(mb_strtolower(T("group.{$name}", $name), "UTF-8"), $term) === 0 or strpos(mb_strtolower(T("group.{$name}.plural", $name), "UTF-8"), $term) === 0) { $group = $id; break; } } // Did we find any matching groups just before? If so, add a WHERE condition to the query to filter by group. if ($group !== false) { if ($group < 0) { $thisCondition[] = "account=:account{$k}"; $sql->bind(":account{$k}", $groups[$group]["name"]); } elseif (!$groups[$group]["private"] or ET::groupModel()->groupIdsAllowedInGroupIds(ET::$session->getGroupIds(), $group, true)) { $sql->from("member_group mg", "mg.memberId=m.memberId", "left"); $thisCondition[] = "mg.groupId=:group{$k}"; $sql->bind(":group{$k}", $group); } } // Also perform a normal LIKE search. $thisCondition[] = "username LIKE :search{$k}"; $sql->bind(":search{$k}", "%" . $term . "%"); $conditions[] = "(" . implode(" OR ", $thisCondition) . ")"; } $sql->where(implode(" AND ", $conditions)); } // Create a query to get the total number of results. Clone the results one to retain the same WHERE conditions. $count = clone $sql; $count = $count->select("COUNT(m.memberId)")->exec()->result(); // Make an array of possible orders for the list. $orders = array("name" => array(T("Name"), "username ASC"), "posts" => array(T("Posts"), "countPosts DESC"), "activity" => array(T("Last active"), "lastActionTime DESC")); // If an invalid orderBy key was provided, just use the first one. if (!isset($orders[$orderBy])) { $orderBy = reset(array_keys($orders)); } // Work out where to start the results from. $page = 0; if ($start) { // If we're ordering by name and the start argument is a single letter... if ($orderBy == "name" and strlen($start) == 1 and ctype_alpha($start)) { // Run a query to get the position of the first member starting with this letter. $start = ET::SQL()->select("COUNT(memberId)", "position")->from("member")->where("STRCMP(username, :username) = -1")->bind(":username", $start)->exec()->result(); $start = min($count - C("esoTalk.members.membersPerPage"), $start); } elseif ($start[0] == "p") { $page = ltrim($start, "p"); $start = C("esoTalk.members.membersPerPage") * ($page - 1); } else { $start = (int) $start; $page = round($start / C("esoTalk.members.membersPerPage")); } // Apply the offset to the query. $start = max(0, $start); $sql->offset($start); } // Finish constructing the query. We want to get a list of member IDs to show as the results. $ids = $sql->select("m.memberId")->limit(C("esoTalk.members.membersPerPage"))->orderBy($orders[$orderBy][1])->exec()->allRows(); foreach ($ids as &$id) { $id = $id["memberId"]; } // Finally, fetch the member data for the members with these IDs. if ($ids) { $members = ET::memberModel()->getByIds($ids); } else { $members = array(); } // If we're ordering by last active, filter out members who have opted out of being displayed on the online list. if ($orderBy == "activity") { foreach ($members as $k => $member) { if (!empty($member["preferences"]["hideOnline"])) { unset($members[$k]); } } } // If we're doing a normal page load... if ($this->responseType === RESPONSE_TYPE_DEFAULT) { // Set the title and make sure this page isn't indexed. $this->title = T("Member List"); $this->addToHead("<meta name='robots' content='noindex, noarchive'/>"); // Work out the canonical URL for this page. $url = "members/{$orderBy}/p{$page}"; $this->canonicalURL = URL($url, true); $this->pushNavigation("members", "members", URL($url)); // Add JavaScript files and variables for the page to use. $this->addJSFile("core/js/scrubber.js"); $this->addJSFile("core/js/members.js"); $this->addJSVar("membersPerPage", C("esoTalk.members.membersPerPage")); $this->addJSVar("countMembers", $count); $this->addJSVar("startFrom", $start); $this->addJSVar("searchString", $searchString); $this->addJSVar("orderBy", $orderBy); $this->addJSLanguage("Sort By"); // Add the default gambits to the gambit cloud: gambit text => css class to apply. $gambits = array("groups" => array(T("group.administrator.plural") => array("gambit-group-administrator", "icon-wrench"), T("group.member.plural") => array("gambit-group-member", "icon-user"), T("group.suspended") => array("gambit-group-suspended", "icon-shield"))); $groups = ET::groupModel()->getAll(); foreach ($groups as $group) { if ($group["private"]) { continue; } $name = $group["name"]; addToArrayString($gambits["groups"], T("group.{$name}.plural", ucfirst($name)), array("gambit-group-{$name}", "icon-tag"), 1); } $this->trigger("constructGambitsMenu", array(&$gambits)); // Construct the gambits menu based on the above arrays. $gambitsMenu = ETFactory::make("menu"); $linkPrefix = "members/" . $orderBy . "/?search="; 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("gambitsMenu", $gambitsMenu); } // Pass data to the view. $this->data("members", $members); $this->data("countMembers", $count); $this->data("startFrom", $start); $this->data("searchString", $searchString); $this->data("orders", $orders); $this->data("orderBy", $orderBy); // On an AJAX request, just render the list, and also pass back the startFrom position. if ($this->responseType === RESPONSE_TYPE_AJAX) { $this->json("startFrom", $start); $this->render("members/list"); } else { $this->render("members/index"); } }
public function handler_conversationsController_constructGambitsMenu($sender, &$gambits) { if (!ET::$session->user) { return; } addToArrayString($gambits["main"], T("gambit.bookmarked"), array("gambit-bookmarked", "icon-bookmark")); }