예제 #1
0
 /**
  * Set a member's bookmarked flag for a conversation.
  *
  * @param array $conversation The conversation to set the flag on. The conversation array's labels
  * 		and bookmarked attribute will be updated.
  * @param int $memberId The member to set the flag for.
  * @param bool $bookmarked Whether or not to set the conversation to bookmarked.
  * @return void
  */
 public function setBookmarked(&$conversation, $memberId, $bookmarked)
 {
     $bookmarked = (bool) $bookmarked;
     $model = ET::conversationModel();
     $model->setStatus($conversation["conversationId"], $memberId, array("bookmarked" => $bookmarked));
     $model->addOrRemoveLabel($conversation, "bookmarked", $bookmarked);
     $conversation["bookmarked"] = $bookmarked;
 }
예제 #2
0
 function setistaatust(&$conversation, $memberId, $unfinished)
 {
     $unfinished = (bool) $unfinished;
     $model = ET::conversationModel();
     $model->setStatus($conversation["conversationId"], $memberId, array("unfinished" => $unfinished));
     #conversationModel
     $model->addOrRemoveLabel($conversation, "unfinished", $unfinished);
     #conversationModel
     $conversation["unfinished"] = $unfinished;
 }
예제 #3
0
파일: plugin.php 프로젝트: m-mori/forum
 public function action_conversationController_unanswer($sender, $conversationId)
 {
     $conversation = ET::conversationModel()->getById($conversationId);
     if (!$conversation or !$sender->validateToken()) {
         return;
     }
     // Stop here with an error if the user isn't allowed to mark the conversation as answered.
     if ($conversation["startMemberId"] != ET::$session->userId and !$conversation["canModerate"]) {
         $sender->renderMessage(T("Error"), T("message.noPermission"));
         return false;
     }
     $model = ET::conversationModel();
     $model->updateById($conversation["conversationId"], array("answered" => 0));
     redirect(URL(R("return", conversationURL($conversation["conversationId"], $conversation["title"]))));
 }
예제 #4
0
파일: plugin.php 프로젝트: ZerGabriel/Likes
 public function action_conversationController_unlike($sender, $postId = false)
 {
     $sender->responseType = RESPONSE_TYPE_JSON;
     if (!$sender->validateToken() or !ET::$session->userId) {
         return;
     }
     // Get the conversation.
     if (!($conversation = ET::conversationModel()->getByPostId($postId))) {
         return false;
     }
     // Get the post.
     $post = ET::postModel()->getById($postId);
     ET::SQL()->delete()->from("like")->where("postId=:postId")->bind(":postId", $post["postId"])->where("memberId=:memberId")->bind(":memberId", ET::$session->userId)->exec();
     unset($post["likes"][ET::$session->userId]);
     $sender->json("names", $this->getNames($post["likes"]));
     $sender->render();
 }
예제 #5
0
 public function handler_conversationModel_addReplyAfter($sender, $conversation, $postId, $content)
 {
     // Only continue if this is the first post.
     if ($conversation["countPosts"] > 1) {
         return;
     }
     // We get all members who have starred the post author and have no unread posts in the conversation.
     $sql = ET::SQL()->from("member_member mm2", "mm2.memberId2=:userId AND mm2.memberId1=m.memberId AND mm2.follow=1 AND mm2.memberId1!=:userId", "inner")->from("member_conversation co", "co.conversationId=:conversationId AND co.type='member' AND co.id=m.memberId", "left")->where("co.lastRead IS NULL OR co.lastRead>=:posts")->bind(":conversationId", $conversation["conversationId"])->bind(":posts", $conversation["countPosts"] - 1)->bind(":userId", ET::$session->userId);
     $members = ET::memberModel()->getWithSQL($sql);
     $data = array("conversationId" => $conversation["conversationId"], "postId" => $postId, "title" => $conversation["title"]);
     $emailData = array("content" => $content);
     foreach ($members as $member) {
         // Check if this member is allowed to view this conversation before sending them a notification.
         $sql = ET::SQL()->select("conversationId")->from("conversation c")->where("conversationId", $conversation["conversationId"]);
         ET::conversationModel()->addAllowedPredicate($sql, $member);
         if (!$sql->exec()->numRows()) {
             continue;
         }
         ET::activityModel()->create("postMember", $member, ET::$session->user, $data, $emailData);
     }
 }
예제 #6
0
 /**
  * Delete a channel and its conversations (or optionally move its conversations to another channel.)
  *
  * @param int $channelId The ID of the channel to delete.
  * @param bool|int $moveToChannelId The ID of the channel to move conversations to, or false to delete them.
  * @return bool true on success, false on error.
  */
 public function deleteById($channelId, $moveToChannelId = false)
 {
     $channelId = (int) $channelId;
     // Do we want to move the conversations to another channel?
     if ($moveToChannelId !== false) {
         // If the channel does exist, move all the conversation over to it.
         if (array_key_exists((int) $moveToChannelId, $this->getAll())) {
             ET::SQL()->update("conversation")->set("channelId", (int) $moveToChannelId)->where("channelId=:channelId")->bind(":channelId", $channelId)->exec();
         } else {
             $this->error("moveToChannelId", "invalidChannel");
         }
     } else {
         ET::conversationModel()->delete(array("channelId" => $channelId));
     }
     if ($this->errorCount()) {
         return false;
     }
     $result = parent::deleteById($channelId);
     // Reset channels in the global cache.
     ET::$cache->remove(self::CACHE_KEY);
     return $result;
 }
예제 #7
0
 /**
  * Start a new converastion. Assumes the creator is the currently logged in user.
  *
  * @param array $data An array of the conversation's details: title, channelId, content.
  * @param array $membersAllowed An array of entities allowed to view the conversation, in the same format
  * 		as the return value of getMembersAllowed()
  * @param bool $isDraft Whether or not the conversation is a draft.
  * @return bool|array An array containing the new conversation ID and the new post ID, or false if
  * 		there was an error.
  */
 public function create($data, $membersAllowed = array(), $isDraft = false)
 {
     // We can't do this if we're not logged in.
     if (!ET::$session->user) {
         return false;
     }
     // If the title is blank but the user is only saving a draft, call it "Untitled conversation."
     if ($isDraft and !$data["title"]) {
         $data["title"] = T("Untitled conversation");
     }
     // Check for errors; validate the title and the post content.
     $this->validate("title", $data["title"], array($this, "validateTitle"));
     $this->validate("content", $data["content"], array(ET::postModel(), "validateContent"));
     $content = $data["content"];
     unset($data["content"]);
     // Flood control!
     if (ET::$session->isFlooding()) {
         $this->error("flooding", "waitToReply");
     }
     // Make sure that we have permission to post in this channel.
     $data["channelId"] = (int) $data["channelId"];
     if (!ET::channelModel()->hasPermission($data["channelId"], "start")) {
         $this->error("channelId", "invalidChannel");
     }
     // Did we encounter any errors? Don't continue.
     if ($this->errorCount()) {
         return false;
     }
     // Add some more data fields to insert into the database.
     $time = time();
     $data["startMemberId"] = ET::$session->userId;
     $data["startTime"] = $time;
     $data["lastPostMemberId"] = ET::$session->userId;
     $data["lastPostTime"] = $time;
     $data["private"] = !empty($membersAllowed);
     $data["countPosts"] = $isDraft ? 0 : 1;
     // Insert the conversation into the database.
     $conversationId = parent::create($data);
     // Update the member's conversation count.
     ET::SQL()->update("member")->set("countConversations", "countConversations + 1", false)->where("memberId", ET::$session->userId)->exec();
     // Update the channel's converastion count.
     ET::SQL()->update("channel")->set("countConversations", "countConversations + 1", false)->where("channelId", $data["channelId"])->exec();
     // Whip up a little array fo conversation details for this model's functions to work with.
     $conversation = array("conversationId" => $conversationId, "title" => $data["title"], "canReply" => true, "labels" => array());
     // Add the first post or save the draft.
     $postId = null;
     if ($isDraft) {
         $this->setDraft($conversation, ET::$session->userId, $content);
     } else {
         $postId = ET::postModel()->create($conversationId, ET::$session->userId, $content);
         // If the conversation is private, send out notifications to the allowed members.
         if (!empty($membersAllowed)) {
             $memberIds = array();
             foreach ($membersAllowed as $member) {
                 if ($member["type"] == "member") {
                     $memberIds[] = $member["id"];
                 }
             }
             ET::conversationModel()->privateAddNotification($conversation, $memberIds, true);
         }
     }
     // If the conversation is private, add the allowed members to the database.
     if (!empty($membersAllowed)) {
         $inserts = array();
         foreach ($membersAllowed as $member) {
             $inserts[] = array($conversationId, $member["type"], $member["id"], 1);
         }
         ET::SQL()->insert("member_conversation")->setMultiple(array("conversationId", "type", "id", "allowed"), $inserts)->setOnDuplicateKey("allowed", 1)->exec();
     }
     return array($conversationId, $postId);
 }
 public function remove($attachmentId)
 {
     if (!$this->validateToken()) {
         return;
     }
     $session = (array) ET::$session->get("attachments");
     if (isset($session[$attachmentId])) {
         unset($session[$attachmentId]);
         ET::$session->store("attachments", $session);
     } else {
         $model = ET::getInstance("attachmentModel");
         $attachment = $model->getById($attachmentId);
         // Make sure the user has permission to edit this post.
         $permission = false;
         if (!empty($attachment["postId"])) {
             $post = ET::postModel()->getById($attachment["postId"]);
             $conversation = ET::conversationModel()->getById($post["conversationId"]);
             $permission = ET::postModel()->canEditPost($post, $conversation);
         } else {
             $permission = ET::$session->userId == $attachment["draftMemberId"];
         }
         if (!$permission) {
             $this->renderMessage(T("Error"), T("message.noPermission"));
             return false;
         }
         // Remove the attachment from the database and filesystem.
         $model->deleteById($attachmentId);
         @unlink($model->path() . $attachmentId . $attachment["secret"]);
     }
 }
 /**
  * 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();
     }
 }
 /**
  * Start a new converastion. Assumes the creator is the currently logged in user.
  *
  * @param array $data An array of the conversation's details: title, channelId, content.
  * @param array $membersAllowed An array of entities allowed to view the conversation, in the same format
  * 		as the return value of getMembersAllowed()
  * @param bool $isDraft Whether or not the conversation is a draft.
  * @return bool|array An array containing the new conversation ID and the new post ID, or false if
  * 		there was an error.
  */
 public function create($data, $membersAllowed = array(), $isDraft = false)
 {
     // We can't do this if we're not logged in.
     if (!ET::$session->user) {
         return false;
     }
     // If the title is blank but the user is only saving a draft, call it "Untitled conversation."
     if ($isDraft and !$data["title"]) {
         $data["title"] = T("Untitled conversation");
     }
     // Check for errors; validate the title and the post content.
     $this->validate("title", $data["title"], array($this, "validateTitle"));
     $this->validate("content", $data["content"], array(ET::postModel(), "validateContent"));
     $content = $data["content"];
     unset($data["content"]);
     // Flood control!
     if (ET::$session->isFlooding()) {
         $this->error("flooding", sprintf(T("message.waitToReply"), C("esoTalk.conversation.timeBetweenPosts")));
     }
     // Make sure that we have permission to post in this channel.
     $data["channelId"] = (int) $data["channelId"];
     if (!ET::channelModel()->hasPermission($data["channelId"], "start")) {
         $this->error("channelId", "invalidChannel");
     }
     // Did we encounter any errors? Don't continue.
     if ($this->errorCount()) {
         return false;
     }
     // Start a notification group. This means that for all notifications sent out until endNotifcationGroup
     // is called, each individual user will receive a maximum of one.
     ET::activityModel()->startNotificationGroup();
     // Add some more data fields to insert into the database.
     $time = time();
     $data["startMemberId"] = ET::$session->userId;
     $data["startTime"] = $time;
     $data["lastPostMemberId"] = ET::$session->userId;
     $data["lastPostTime"] = $time;
     $data["private"] = !empty($membersAllowed);
     $data["countPosts"] = $isDraft ? 0 : 1;
     // Insert the conversation into the database.
     $conversationId = parent::create($data);
     // Update the member's conversation count.
     ET::SQL()->update("member")->set("countConversations", "countConversations + 1", false)->where("memberId", ET::$session->userId)->exec();
     // Update the channel's converastion count.
     ET::SQL()->update("channel")->set("countConversations", "countConversations + 1", false)->where("channelId", $data["channelId"])->exec();
     // Get our newly created conversation.
     $conversation = $this->getById($conversationId);
     // Add the first post or save the draft.
     $postId = null;
     if ($isDraft) {
         $this->setDraft($conversation, ET::$session->userId, $content);
     } else {
         $postId = ET::postModel()->create($conversationId, ET::$session->userId, $content, $conversation["title"]);
         // If the conversation is private, send out notifications to the allowed members.
         if (!empty($membersAllowed)) {
             $memberIds = array();
             foreach ($membersAllowed as $member) {
                 if ($member["type"] == "member") {
                     $memberIds[] = $member["id"];
                 }
             }
             ET::conversationModel()->privateAddNotification($conversation, $memberIds, true, $content);
         }
     }
     // If the conversation is private, add the allowed members to the database.
     if (!empty($membersAllowed)) {
         $inserts = array();
         foreach ($membersAllowed as $member) {
             $inserts[] = array($conversationId, $member["type"], $member["id"], 1);
         }
         ET::SQL()->insert("member_conversation")->setMultiple(array("conversationId", "type", "id", "allowed"), $inserts)->setOnDuplicateKey("allowed", 1)->exec();
     }
     // If the user has the "star on reply" or "star private" preferences checked, star the conversation.
     if (ET::$session->preference("starOnReply") or $conversation["private"] and ET::$session->preference("starPrivate")) {
         $this->setStatus($conversation["conversationId"], ET::$session->userId, array("starred" => true));
     }
     $this->trigger("createAfter", array($conversation, $postId, $content));
     ET::activityModel()->endNotificationGroup();
     return array($conversationId, $postId);
 }
예제 #11
0
 /**
  * 
  *
  * 
  *
  * @package esoTalk
  */
 function label($label, $url = "", $className = "")
 {
     // Make sure the ETConversationModel class has been loaded so we can access its static properties.
     ET::conversationModel();
     return ($url ? "<a href='{$url}'" : "<span") . " class='label label-{$label} {$className}' title='" . T("label.{$label}") . "'>\n\t\t<i class='" . ETConversationModel::$labels[$label][1] . "'></i>\n\t</" . ($url ? "a" : "span") . ">";
 }
예제 #12
0
 /**
  * Create a post in the specified conversation.
  *
  * This function will go through the post content and notify any members who are @mentioned.
  *
  * @param int $conversationId The ID of the conversation to create the post in.
  * @param int $memberId The ID of the author of the post.
  * @param string $content The post content.
  * @param string $title The title of the conversation (so it can be added alongside the post, for fulltext purposes.)
  * @return bool|int The new post's ID, or false if there were errors.
  */
 public function create($conversationId, $memberId, $content, $title = "")
 {
     // Validate the post content.
     $this->validate("content", $content, array($this, "validateContent"));
     if ($this->errorCount()) {
         return false;
     }
     // Prepare the post details for the query.
     $data = array("conversationId" => $conversationId, "memberId" => $memberId, "time" => time(), "content" => $content, "title" => $title);
     $id = parent::create($data);
     // Update the member's post count.
     ET::SQL()->update("member")->set("countPosts", "countPosts + 1", false)->where("memberId", $memberId)->exec();
     // Update the channel's post count.
     ET::SQL()->update("channel")->set("countPosts", "countPosts + 1", false)->where("channelId", ET::SQL()->select("channelId")->from("conversation")->where("conversationId=:conversationId")->bind(":conversationId", $conversationId)->exec()->result())->exec();
     // Parse the post content for @mentions, and notify any members who were mentioned.
     if (C("esoTalk.format.mentions")) {
         $names = ET::formatter()->getMentions($content);
         if (count($names)) {
             // Get the member details from the database.
             $sql = ET::SQL()->where("m.username IN (:names)")->bind(":names", $names)->where("m.memberId != :userId")->bind(":userId", $memberId);
             $members = ET::memberModel()->getWithSQL($sql);
             $data = array("conversationId" => $conversationId, "postId" => (int) $id, "title" => $title);
             $emailData = array("content" => $content);
             $i = 0;
             foreach ($members as $member) {
                 // Only send notifications to the first 10 members who are mentioned to prevent abuse of the system.
                 if ($i++ > 10) {
                     break;
                 }
                 // Check if this member is allowed to view this conversation before sending them a notification.
                 $sql = ET::SQL()->select("conversationId")->from("conversation c")->where("conversationId", $conversationId);
                 ET::conversationModel()->addAllowedPredicate($sql, $member);
                 if (!$sql->exec()->numRows()) {
                     continue;
                 }
                 ET::activityModel()->create("mention", $member, ET::$session->user, $data, $emailData);
             }
         }
     }
     return $id;
 }
예제 #13
0
 /**
  * Perform a fresh installation of the esoTalk database. Create the table structure and insert default data.
  *
  * @param array $info An array of information gathered from the installation form.
  * @return void
  */
 public function install($info)
 {
     // Create the table structure.
     $this->structure(true);
     // 2016/02 インストール時処理 修正
     // admin ユーザ登録は不要
     //	// Create the administrator member.
     //	$member = array(
     //		"username" => $info["adminUser"],
     //		"email" => $info["adminEmail"],
     //		"password" => $info["adminPass"],
     //		"account" => "Administrator",
     //		"confirmed" => true
     //	);
     //	ET::memberModel()->create($member);
     //
     //	// Set the session's userId and user information variables to the administrator, so that all entities
     //	// created below will be created by the administrator user.
     //	ET::$session->userId = 1;
     //	ET::$session->user = ET::memberModel()->getById(1);
     // Create the moderator group.
     ET::groupModel()->create(array("name" => "Moderator", "canSuspend" => true));
     // Create the General Discussion channel.
     $id = ET::channelModel()->create(array("title" => "全般", "slug" => slug("General Discussion")));
     ET::channelModel()->setPermissions($id, array(GROUP_ID_GUEST => array("view" => true), GROUP_ID_MEMBER => array("view" => true, "reply" => true, "start" => true), 1 => array("view" => true, "reply" => true, "start" => true, "moderate" => true)));
     // Create the Staff Only channel.
     $id = ET::channelModel()->create(array("title" => "Staff Only", "slug" => slug("Staff Only")));
     ET::channelModel()->setPermissions($id, array(1 => array("view" => true, "reply" => true, "start" => true, "moderate" => true)));
     // Set the flood control config setting to zero so that we can create two conversations in a row.
     ET::$config["esoTalk.conversation.timeBetweenPosts"] = 0;
     // Create a welcome conversation.
     ET::conversationModel()->create(array("title" => "Welcome to " . $info["forumTitle"] . "!", "content" => "[b]Welcome to " . $info["forumTitle"] . "![/b]\n\n" . $info["forumTitle"] . " is powered by [url=http://esotalk.org]esoTalk[/url], the simple, fast, free web-forum.\n\nFeel free to edit or delete this conversation. Otherwise, it's time to get posting!\n\nAnyway, good luck, and we hope you enjoy using esoTalk.", "channelId" => 1));
     // Create a helpful private conversation with the administrator.
     ET::conversationModel()->create(array("title" => "Pssst! Want a few tips?", "content" => "Hey {$info["adminUser"]}, congrats on getting esoTalk installed!\n\nCool! Your forum is now good-to-go, but you might want to customize it with your own logo, design, and settings—so here's how.\n\n[h]Changing the Logo[/h]\n\n1. Go to the [url=" . C("esoTalk.baseURL") . "admin/settings]Forum Settings[/url] section of your administration panel.\n2. Select 'Show an image in the header' for the 'Forum header' setting.\n3. Find and select the image file you wish to use.\n4. Click 'Save Changes'. The logo will automatically be resized so it fits nicely in the header.\n\n[h]Changing the Appearance[/h]\n\n1. Go to the [url=" . C("esoTalk.baseURL") . "admin/appearance]Appearance[/url] section of your administration panel.\n2. Choose colors for the header, page background, or select a background image. (More skins will be available soon.)\n3. Click 'Save Changes', and your forum's appearance will be updated!\n\n[h]Managing Channels[/h]\n\n'Channels' are a way to categorize conversations in your forum. You can create as many or as few channels as you like, nest them, and give them custom permissions.\n\n1. Go to the [url=" . C("esoTalk.baseURL") . "admin/channels]Channels[/url] section of your administration panel.\n2. Click 'Create Channel' and fill out a title, description, and select permissions to add a new channel.\n3. Drag and drop channels to rearrange and nest them.\n\n[h]Getting Help[/h]\n\nIf you need help, come and give us a yell at the [url=http://esotalk.org/forum]esoTalk Support Forum[/url]. Don't worry—we don't bite!", "channelId" => 1), array(array("type" => "member", "id" => 1)));
     // All done!
 }
 /**
  * Shortcut function to get a conversation and render a 404 page if it cannot be found.
  *
  * @param int $id The ID of the conversation to get, or the post to get the conversation of.
  * @param bool $post Whether or not $id is the conversationId or a postId.
  * @return bool|array An array of the conversation details, or false if it wasn't found.
  */
 public function getConversation($id, $post = false)
 {
     $conversation = !$post ? ET::conversationModel()->getById($id) : ET::conversationModel()->getByPostId($id);
     // Stop here if the conversation doesn't exist, or if the user is not allowed to view it.
     if (!$conversation) {
         $this->render404(T("message.conversationNotFound"));
         return false;
     }
     return $conversation;
 }
예제 #15
0
 public function action_remove($attachmentId)
 {
     if (!$this->validateToken()) {
         return;
     }
     //		$session = (array)ET::$session->get("attachments");
     $model = ET::getInstance("attachmentModel");
     $attachment = $model->getById($attachmentId);
     $sessId = ET::$session->getSessId();
     if (is_array($attachment) && count($attachment) > 0) {
         // Make sure the user has permission to edit this post.
         $permission = false;
         if (!empty($attachment["postId"])) {
             $post = ET::postModel()->getById($attachment["postId"]);
             $conversation = ET::conversationModel()->getById($post["conversationId"]);
             $permission = ET::postModel()->canEditPost($post, $conversation);
         } else {
             if ($attachment["sessId"] == $sessId) {
                 $permission = true;
             } else {
                 $permission = ET::$session->userId == $attachment["draftMemberId"];
             }
         }
         if (!$permission) {
             $this->renderMessage(T("Error"), T("message.noPermission"));
             return false;
         }
         // Remove the attachment from the database.
         $model->deleteById($attachmentId);
         // Remove the attachment from the filesystem.
         $model->removeFile($attachment);
     }
 }
예제 #16
0
 /**
  * Get a full list of conversation details for a list of conversation IDs.
  *
  * @param array $conversationIDs The list of conversation IDs to fetch details for.
  * @param bool $checkForPermission Whether or not to add a check onto the query to make sure the
  * 		user has permission to view all of the conversations.
  */
 public function getResults($conversationIDs, $checkForPermission = false)
 {
     // Construct a query to get details for all of the specified conversations.
     $sql = ET::SQL()->select("s.*")->select("c.*")->select("sm.memberId", "startMemberId")->select("sm.username", "startMember")->select("sm.avatarFormat", "startMemberAvatarFormat")->select("lpm.memberId", "lastPostMemberId")->select("lpm.username", "lastPostMember")->select("lpm.email", "lastPostMemberEmail")->select("lpm.avatarFormat", "lastPostMemberAvatarFormat")->select("IF((IF(c.lastPostTime IS NOT NULL,c.lastPostTime,c.startTime)>:markedAsRead AND (s.lastRead IS NULL OR s.lastRead<c.countPosts)),(c.countPosts - IF(s.lastRead IS NULL,0,s.lastRead)),0)", "unread")->select("p.content", "firstPost")->from("conversation c")->from("member_conversation s", "s.conversationId=c.conversationId AND s.type='member' AND s.id=:memberId", "left")->from("member sm", "c.startMemberId=sm.memberId", "left")->from("member lpm", "c.lastPostMemberId=lpm.memberId", "left")->from("channel ch", "c.channelId=ch.channelId", "left")->from("post p", "c.conversationId=p.conversationId AND c.startTime=p.time", "left")->bind(":markedAsRead", ET::$session->preference("markedAllConversationsAsRead"))->bind(":memberId", ET::$session->userId);
     // If we need to, filter out all conversations that the user isn't allowed to see.
     if ($checkForPermission) {
         ET::conversationModel()->addAllowedPredicate($sql);
     }
     // Add a labels column to the query.
     ET::conversationModel()->addLabels($sql);
     // Limit the results to the specified conversation IDs
     $sql->where("c.conversationId IN (:conversationIds)")->orderBy("FIELD(c.conversationId,:conversationIdsOrder)");
     $sql->bind(":conversationIds", $conversationIDs, PDO::PARAM_INT);
     $sql->bind(":conversationIdsOrder", $conversationIDs, PDO::PARAM_INT);
     $this->trigger("beforeGetResults", array(&$sql));
     // Execute the query and put the details of the conversations into an array.
     $result = $sql->exec();
     $results = array();
     $model = ET::conversationModel();
     while ($row = $result->nextRow()) {
         // Expand the comma-separated label flags into a workable array of active labels.
         $row["labels"] = $model->expandLabels($row["labels"]);
         $row["replies"] = max(0, $row["countPosts"] - 1);
         $results[] = $row;
     }
     $this->trigger("afterGetResults", array(&$results));
     return $results;
 }
예제 #17
0
 /**
  * 会話IDから対応するテーマ(スレッド)のチャンネルIDを取得
  * @param type $conversationId
  * @return type
  */
 public function getChannelId($conversationId)
 {
     $channelId = "";
     if ($conversationId) {
         $conModel = ET::conversationModel();
         $result = ET::SQL()->select("channelId")->from("conversation")->where("conversationId", $conversationId)->exec()->firstRow();
         if ($result) {
             $channelId = $result["channelId"];
         }
     }
     return $channelId;
 }