From 8e086d5a90b2775a2aa0b44fad160086cb206922 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 1 Apr 2011 23:59:35 -0700 Subject: [PATCH 01/20] QnA - save answer revisions and show # of revisions --- plugins/QnA/QnAPlugin.php | 4 + plugins/QnA/actions/qnanewanswer.php | 4 +- plugins/QnA/actions/qnareviseanswer.php | 205 ++++++++++++++++++++++++ plugins/QnA/classes/QnA_Answer.php | 12 +- plugins/QnA/classes/QnA_Question.php | 4 +- plugins/QnA/lib/qnareviseanswerform.php | 11 +- 6 files changed, 229 insertions(+), 11 deletions(-) create mode 100644 plugins/QnA/actions/qnareviseanswer.php diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index 9a05eeb0b2..5740054a5f 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -124,6 +124,10 @@ class QnAPlugin extends MicroAppPlugin 'main/qna/newanswer', array('action' => 'qnanewanswer') ); + $m->connect( + 'main/qna/reviseanswer', + array('action' => 'qnareviseanswer') + ); $m->connect( 'question/vote/:id', array('action' => 'qnavote', 'type' => 'question'), diff --git a/plugins/QnA/actions/qnanewanswer.php b/plugins/QnA/actions/qnanewanswer.php index 09d111040d..a91ae9ad39 100644 --- a/plugins/QnA/actions/qnanewanswer.php +++ b/plugins/QnA/actions/qnanewanswer.php @@ -20,7 +20,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - * @category QuestonAndAnswer + * @category QnA * @package StatusNet * @author Zach Copley * @copyright 2011 StatusNet, Inc. @@ -158,7 +158,7 @@ class QnanewanswerAction extends Action $this->element('title', null, _m('Answers')); $this->elementEnd('head'); $this->elementStart('body'); - $this->raw() + $this->raw($this->answer->asHTML()); $this->elementEnd('body'); $this->elementEnd('html'); } else { diff --git a/plugins/QnA/actions/qnareviseanswer.php b/plugins/QnA/actions/qnareviseanswer.php new file mode 100644 index 0000000000..686cf8d46d --- /dev/null +++ b/plugins/QnA/actions/qnareviseanswer.php @@ -0,0 +1,205 @@ +. + * + * @category QnA + * @package StatusNet + * @author Zach Copley + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * Revise an answer + * + * @category QnA + * @package StatusNet + * @author Zach Copley + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ +class QnareviseanswerAction extends Action +{ + protected $user = null; + protected $error = null; + protected $question = null; + protected $answer = null; + protected $content = null; + + /** + * Returns the title of the action + * + * @return string Action title + */ + function title() + { + // TRANS: Page title for revising a question + return _m('Revise answer'); + } + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + function prepare($argarray) + { + parent::prepare($argarray); + if ($this->boolean('ajax')) { + StatusNet::setApi(true); + } + + $this->user = common_current_user(); + + if (empty($this->user)) { + // TRANS: Client exception thrown trying to answer a question while not logged in. + throw new ClientException( + _m("You must be logged in to answer to a question."), + 403 + ); + } + + if ($this->isPost()) { + $this->checkSessionToken(); + } + + $id = substr($this->trimmed('id'), 7); + + common_debug("XXXXXXXXXXXXXXXXXX id = " . $id); + + $this->answer = QnA_Answer::staticGet('id', $id); + $this->question = $this->answer->getQuestion(); + + if (empty($this->answer) || empty($this->question)) { + // TRANS: Client exception thrown trying to respond to a non-existing question. + throw new ClientException( + _m('Invalid or missing answer.'), + 404 + ); + } + + $this->answerText = $this->trimmed('answer'); + + return true; + } + + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return void + */ + function handle($argarray=null) + { + parent::handle($argarray); + + if ($this->isPost()) { + $this->reviseAnswer(); + } else { + $this->showPage(); + } + + return; + } + + /** + * Revise the answer + * + * @return void + */ + function reviseAnswer() + { + $answer = $this->answer; + + try { + $orig = clone($answer); + $answer->content = $this->answerText; + $answer->revisions++; + $result = $answer->update($orig); + } catch (ClientException $ce) { + $this->error = $ce->getMessage(); + $this->showPage(); + return; + } + if ($this->boolean('ajax')) { + common_debug("ajaxy part"); + header('Content-Type: text/xml;charset=utf-8'); + $this->xw->startDocument('1.0', 'UTF-8'); + $this->elementStart('html'); + $this->elementStart('head'); + // TRANS: Page title after sending an answer. + $this->element('title', null, _m('Answer')); + $this->elementEnd('head'); + $this->elementStart('body'); + $this->raw($answer->asHTML()); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect($this->answer->bestUrl(), 303); + } + } + + /** + * Show the revise answer form + * + * @return void + */ + function showContent() + { + if (!empty($this->error)) { + $this->element('p', 'error', $this->error); + } + + $form = new QnareviseanswerForm($this->answer, $this); + $form->show(); + + return; + } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + function isReadOnly($args) + { + if ($_SERVER['REQUEST_METHOD'] == 'GET' || + $_SERVER['REQUEST_METHOD'] == 'HEAD') { + return true; + } else { + return false; + } + } +} diff --git a/plugins/QnA/classes/QnA_Answer.php b/plugins/QnA/classes/QnA_Answer.php index 5727411a80..79970dc1df 100644 --- a/plugins/QnA/classes/QnA_Answer.php +++ b/plugins/QnA/classes/QnA_Answer.php @@ -205,8 +205,16 @@ class QnA_Answer extends Managed_DataObject { $notice = $question->getNotice(); - $fmt = 'answer by %3$s'; - $fmt .= '%4$s'; + $fmt = '

'; + $fmt .= 'answer by %3$s'; + $fmt .= '%4$s'; + if (!empty($answer->revisions)) { + $fmt .= '' + . $answer->revisions + . _m('revisions') + . ''; + } + $fmt .= '

'; return sprintf( $fmt, diff --git a/plugins/QnA/classes/QnA_Question.php b/plugins/QnA/classes/QnA_Question.php index 2403857d28..0446128ea0 100644 --- a/plugins/QnA/classes/QnA_Question.php +++ b/plugins/QnA/classes/QnA_Question.php @@ -221,11 +221,11 @@ class QnA_Question extends Managed_DataObject { $notice = $question->getNotice(); - $fmt = '
'; + $fmt = '

'; $fmt .= '%2$s'; $fmt .= '%3$s'; $fmt .= 'asked by %5$s'; - $fmt .= '

'; + $fmt .= '

'; $q = sprintf( $fmt, diff --git a/plugins/QnA/lib/qnareviseanswerform.php b/plugins/QnA/lib/qnareviseanswerform.php index 48f47e5e98..f9ebae132c 100644 --- a/plugins/QnA/lib/qnareviseanswerform.php +++ b/plugins/QnA/lib/qnareviseanswerform.php @@ -91,7 +91,7 @@ class QnareviseanswerForm extends Form */ function action() { - return common_local_url('qnareviseanswer', array('id' => $this->question->id)); + return common_local_url('qnareviseanswer'); } /** @@ -101,12 +101,13 @@ class QnareviseanswerForm extends Form */ function formData() { - $question = $this->question; $out = $this->out; - $id = "question-" . $question->id; - $out->element('p', 'Your answer to:', $question->title); - $out->textarea('answerText', 'You said:', $this->answer->content); + $out->element('p', 'Your answer to:', $this->question->title); + + $id = "answer-" . $this->answer->id; + $out->hidden('id', $id); + $out->textarea('answer', 'You said:', $this->answer->content); } /** From bac112c244b6c9a311110e920912ebfb84fab7d5 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sun, 3 Apr 2011 16:57:59 -0700 Subject: [PATCH 02/20] QnA - Better display of questions and answers in streams --- plugins/QnA/QnAPlugin.php | 92 ++++++++++++++++++++++++---- plugins/QnA/actions/qnanewanswer.php | 7 ++- 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index 5740054a5f..e53d56928c 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -290,6 +290,50 @@ class QnAPlugin extends MicroAppPlugin return true; } + /** + * Output our CSS class for QnA notice list elements + * + * @param NoticeListItem $nli The item being shown + * + * @return boolean hook value + */ + + function onStartOpenNoticeListItemElement($nli) + { + + $type = $nli->notice->object_type; + + switch($type) + { + case QnA_Question::OBJECT_TYPE: + $id = (empty($nli->repeat)) ? $nli->notice->id : $nli->repeat->id; + $nli->out->elementStart( + 'li', array( + 'class' => 'hentry notice question', + 'id' => 'notice-' . $id + ) + ); + Event::handle('EndOpenNoticeListItemElement', array($nli)); + return false; + break; + case QnA_Answer::OBJECT_TYPE: + $id = (empty($nli->repeat)) ? $nli->notice->id : $nli->repeat->id; + $nli->out->elementStart( + 'li', array( + 'class' => 'hentry notice answer', + 'id' => 'notice-' . $id + ) + ); + Event::handle('EndOpenNoticeListItemElement', array($nli)); + return false; + break; + default: + return true; + } + + return true; + } + /** * Custom HTML output for our notices * @@ -323,23 +367,34 @@ class QnAPlugin extends MicroAppPlugin $nli = new NoticeListItem($notice, $out); $nli->showNotice(); - $out->elementStart('div', array('class' => 'entry-content question-content')); + + + $out->elementStart('div', array('class' => 'entry-content question-desciption')); + $question = QnA_Question::getByNotice($notice); - if ($question) { - if ($user) { - $profile = $user->getProfile(); - $answer = $question->getAnswer($profile); - if ($answer) { - // User has already answer; show the results. - $form = new QnareviseanswerForm($answer, $out); - } else { - $form = new QnaanswerForm($question, $out); + if (!empty($question)) { + + $short = $question->description; + $out->raw($question->description); + + // Don't prompt user for an answer if the question is closed or + // the current user posed the question in the first place + if (empty($question->closed)) { + if (!empty($user) && ($user->id != $question->profile_id)) { + $profile = $user->getProfile(); + $answer = $question->getAnswer($profile); + if ($answer) { + // User has already answered; show the results. + $form = new QnareviseanswerForm($answer, $out); + } else { + $form = new QnaanswerForm($question, $out); + } + $form->show(); } - $form->show(); } } else { - $out->text(_m('Question data is missing')); + $out->text(_m('Question data is missing.')); } $out->elementEnd('div'); @@ -355,6 +410,19 @@ class QnAPlugin extends MicroAppPlugin $nli = new NoticeListItem($notice, $out); $nli->showNotice(); + $out->elementStart('div', array('class' => 'entry-content answer-content')); + + $answer = QnA_Answer::staticGet('uri', $notice->uri); + + if (!empty($answer)) { + $short = $answer->content; + $out->raw($answer->content); + } else { + $out->text(_m('Answer data is missing.')); + } + + $out->elementEnd('div'); + // @fixme $out->elementStart('div', array('class' => 'entry-content')); } diff --git a/plugins/QnA/actions/qnanewanswer.php b/plugins/QnA/actions/qnanewanswer.php index a91ae9ad39..b4db9cadda 100644 --- a/plugins/QnA/actions/qnanewanswer.php +++ b/plugins/QnA/actions/qnanewanswer.php @@ -137,9 +137,11 @@ class QnanewanswerAction extends Action */ function newAnswer() { + $profile = $this->user->getProfile(); + try { $notice = QnA_Answer::saveNew( - $this->user->getProfile(), + $profile, $this->question, $this->answerText ); @@ -150,6 +152,7 @@ class QnanewanswerAction extends Action } if ($this->boolean('ajax')) { common_debug("ajaxy part"); + $answer = $this->question->getAnswer($profile); header('Content-Type: text/xml;charset=utf-8'); $this->xw->startDocument('1.0', 'UTF-8'); $this->elementStart('html'); @@ -158,7 +161,7 @@ class QnanewanswerAction extends Action $this->element('title', null, _m('Answers')); $this->elementEnd('head'); $this->elementStart('body'); - $this->raw($this->answer->asHTML()); + $this->raw($answer->asHTML()); $this->elementEnd('body'); $this->elementEnd('html'); } else { From 960aebdbc4c7d8b61ced0e545cc1facc02f7f168 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sun, 3 Apr 2011 17:25:44 -0700 Subject: [PATCH 03/20] QnA - add best class to best answers --- plugins/QnA/QnAPlugin.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index e53d56928c..f3a07f13c5 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -300,7 +300,6 @@ class QnAPlugin extends MicroAppPlugin function onStartOpenNoticeListItemElement($nli) { - $type = $nli->notice->object_type; switch($type) @@ -318,9 +317,18 @@ class QnAPlugin extends MicroAppPlugin break; case QnA_Answer::OBJECT_TYPE: $id = (empty($nli->repeat)) ? $nli->notice->id : $nli->repeat->id; + + $classes = array('hentry', 'notice', 'answer'); + + $answer = QnA_Answer::staticGet('uri', $notice->uri); + + if (!empty($answer) && (boolean($answer->best))) { + $classes[] = 'best'; + } + $nli->out->elementStart( 'li', array( - 'class' => 'hentry notice answer', + 'class' => implode(' ', $classes), 'id' => 'notice-' . $id ) ); @@ -367,8 +375,6 @@ class QnAPlugin extends MicroAppPlugin $nli = new NoticeListItem($notice, $out); $nli->showNotice(); - - $out->elementStart('div', array('class' => 'entry-content question-desciption')); $question = QnA_Question::getByNotice($notice); From 528d999ae757d382c00a88baac493f3c51e11d3b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 01:27:38 -0700 Subject: [PATCH 04/20] QnA - Allow answer revisions and marking a question as "best" --- plugins/QnA/QnAPlugin.php | 61 ++++-- plugins/QnA/actions/qnanewanswer.php | 2 +- plugins/QnA/actions/qnareviseanswer.php | 94 +++++++-- plugins/QnA/classes/QnA_Answer.php | 11 +- ...qnaanswerform.php => qnanewanswerform.php} | 2 +- plugins/QnA/lib/qnashowanswerform.php | 181 ++++++++++++++++++ 6 files changed, 315 insertions(+), 36 deletions(-) rename plugins/QnA/lib/{qnaanswerform.php => qnanewanswerform.php} (98%) create mode 100644 plugins/QnA/lib/qnashowanswerform.php diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index f3a07f13c5..08a73031a5 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -88,9 +88,11 @@ class QnAPlugin extends MicroAppPlugin . strtolower(mb_substr($cls, 0, -6)) . '.php'; return false; case 'QnaquestionForm': - case 'QnaanswerForm': + case 'QnashowanswerForm': + case 'QnanewanswerForm': case 'QnareviseanswerForm': case 'QnavoteForm': + case 'AnswerNoticeListItem': include_once $dir . '/lib/' . strtolower($cls).'.php'; break; case 'QnA_Question': @@ -318,17 +320,18 @@ class QnAPlugin extends MicroAppPlugin case QnA_Answer::OBJECT_TYPE: $id = (empty($nli->repeat)) ? $nli->notice->id : $nli->repeat->id; - $classes = array('hentry', 'notice', 'answer'); + $cls = array('hentry', 'notice', 'answer'); $answer = QnA_Answer::staticGet('uri', $notice->uri); - if (!empty($answer) && (boolean($answer->best))) { - $classes[] = 'best'; + if (!empty($answer) && !empty($answer->best)) { + $cls[] = 'best'; } $nli->out->elementStart( - 'li', array( - 'class' => implode(' ', $classes), + 'li', + array( + 'class' => implode(' ', $cls), 'id' => 'notice-' . $id ) ); @@ -348,6 +351,7 @@ class QnAPlugin extends MicroAppPlugin * @param Notice $notice * @param HTMLOutputter $out */ + function showNotice($notice, $out) { switch ($notice->object_type) { @@ -381,8 +385,8 @@ class QnAPlugin extends MicroAppPlugin if (!empty($question)) { - $short = $question->description; - $out->raw($question->description); + $short = $this->shorten($question->description, $notice); + $out->raw($short); // Don't prompt user for an answer if the question is closed or // the current user posed the question in the first place @@ -390,14 +394,13 @@ class QnAPlugin extends MicroAppPlugin if (!empty($user) && ($user->id != $question->profile_id)) { $profile = $user->getProfile(); $answer = $question->getAnswer($profile); - if ($answer) { - // User has already answered; show the results. - $form = new QnareviseanswerForm($answer, $out); - } else { - $form = new QnaanswerForm($question, $out); + if (!$answer) { + $form = new QnanewanswerForm($question, $out); + $form->show(); } - $form->show(); } + } else { + $out->element('span', 'closed', _m('This question is closed.')); } } else { $out->text(_m('Question data is missing.')); @@ -411,18 +414,18 @@ class QnAPlugin extends MicroAppPlugin function showNoticeAnswer($notice, $out) { $user = common_current_user(); + + $answer = QnA_Answer::getByNotice($notice); + $question = $answer->getQuestion(); - // @hack we want regular rendering, then just add stuff after that $nli = new NoticeListItem($notice, $out); $nli->showNotice(); $out->elementStart('div', array('class' => 'entry-content answer-content')); - $answer = QnA_Answer::staticGet('uri', $notice->uri); - if (!empty($answer)) { - $short = $answer->content; - $out->raw($answer->content); + $form = new QnashowanswerForm($out, $answer); + $form->show(); } else { $out->text(_m('Answer data is missing.')); } @@ -433,6 +436,26 @@ class QnAPlugin extends MicroAppPlugin $out->elementStart('div', array('class' => 'entry-content')); } + static function shorten($content, $notice) + { + $short = null; + + if (Notice::contentTooLong($content)) { + common_debug("content too long"); + $max = Notice::maxContent(); + $short = mb_substr($content, 0, $max - 1); + $short .= sprintf( + '', + $notice->uri, + _m('more') + ); + } else { + $short = $content; + } + + return $short; + } + /** * Form for our app * diff --git a/plugins/QnA/actions/qnanewanswer.php b/plugins/QnA/actions/qnanewanswer.php index b4db9cadda..94bfc09a39 100644 --- a/plugins/QnA/actions/qnanewanswer.php +++ b/plugins/QnA/actions/qnanewanswer.php @@ -180,7 +180,7 @@ class QnanewanswerAction extends Action $this->element('p', 'error', $this->error); } - $form = new QnaanswerForm($this->question, $this); + $form = new QnanewanswerForm($this->question, $this); $form->show(); return; diff --git a/plugins/QnA/actions/qnareviseanswer.php b/plugins/QnA/actions/qnareviseanswer.php index 686cf8d46d..cea1258e0d 100644 --- a/plugins/QnA/actions/qnareviseanswer.php +++ b/plugins/QnA/actions/qnareviseanswer.php @@ -86,14 +86,8 @@ class QnareviseanswerAction extends Action ); } - if ($this->isPost()) { - $this->checkSessionToken(); - } - $id = substr($this->trimmed('id'), 7); - common_debug("XXXXXXXXXXXXXXXXXX id = " . $id); - $this->answer = QnA_Answer::staticGet('id', $id); $this->question = $this->answer->getQuestion(); @@ -122,12 +116,22 @@ class QnareviseanswerAction extends Action parent::handle($argarray); if ($this->isPost()) { - $this->reviseAnswer(); - } else { - $this->showPage(); + $this->checkSessionToken(); + if ($this->arg('revise')) { + $this->showContent(); + return; + } else if ($this->arg('best')) { + if ($this->user->id == $this->question->profile_id) { + $this->markBest(); + return; + } + } else { + $this->reviseAnswer(); + return; + } } - return; + $this->showPage(); } /** @@ -159,7 +163,52 @@ class QnareviseanswerAction extends Action $this->element('title', null, _m('Answer')); $this->elementEnd('head'); $this->elementStart('body'); - $this->raw($answer->asHTML()); + $form = new QnashowanswerForm($this, $answer); + $form->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect($this->answer->bestUrl(), 303); + } + } + + /** + * Mark the answer as the "best" answer + * + * @return void + */ + function markBest() + { + $question = $this->question; + $answer = $this->answer; + + try { + // close the question to further answers + $orig = clone($question); + $question->closed = 1; + $result = $question->update($orig); + + // mark this answer an the best answer + $orig = clone($answer); + $answer->best = 1; + $result = $answer->update($orig); + } catch (ClientException $ce) { + $this->error = $ce->getMessage(); + $this->showPage(); + return; + } + if ($this->boolean('ajax')) { + common_debug("ajaxy part"); + header('Content-Type: text/xml;charset=utf-8'); + $this->xw->startDocument('1.0', 'UTF-8'); + $this->elementStart('html'); + $this->elementStart('head'); + // TRANS: Page title after sending an answer. + $this->element('title', null, _m('Answer')); + $this->elementEnd('head'); + $this->elementStart('body'); + $form = new QnashowanswerForm($this, $answer); + $form->show(); $this->elementEnd('body'); $this->elementEnd('html'); } else { @@ -178,12 +227,31 @@ class QnareviseanswerAction extends Action $this->element('p', 'error', $this->error); } - $form = new QnareviseanswerForm($this->answer, $this); - $form->show(); + if ($this->boolean('ajax')) { + $this->showAjaxReviseForm(); + } else { + $form = new QnareviseanswerForm($this->answer, $this); + $form->show(); + } return; } + function showAjaxReviseForm() + { + header('Content-Type: text/xml;charset=utf-8'); + $this->xw->startDocument('1.0', 'UTF-8'); + $this->elementStart('html'); + $this->elementStart('head'); + $this->element('title', null, _m('Answer')); + $this->elementEnd('head'); + $this->elementStart('body'); + $form = new QnareviseanswerForm($this->answer, $this); + $form->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } + /** * Return true if read only. * diff --git a/plugins/QnA/classes/QnA_Answer.php b/plugins/QnA/classes/QnA_Answer.php index 79970dc1df..a2333be6dc 100644 --- a/plugins/QnA/classes/QnA_Answer.php +++ b/plugins/QnA/classes/QnA_Answer.php @@ -140,7 +140,7 @@ class QnA_Answer extends Managed_DataObject { $answer = self::staticGet('uri', $notice->uri); if (empty($answer)) { - throw new Exception("No answer with URI {$this->notice->uri}"); + throw new Exception("No answer with URI {$notice->uri}"); } return $answer; } @@ -205,7 +205,14 @@ class QnA_Answer extends Managed_DataObject { $notice = $question->getNotice(); - $fmt = '

'; + $fmt = ''; + + if (!empty($answer->best)) { + $fmt = '

'; + } else { + $fmt = '

'; + } + $fmt .= 'answer by %3$s'; $fmt .= '%4$s'; if (!empty($answer->revisions)) { diff --git a/plugins/QnA/lib/qnaanswerform.php b/plugins/QnA/lib/qnanewanswerform.php similarity index 98% rename from plugins/QnA/lib/qnaanswerform.php rename to plugins/QnA/lib/qnanewanswerform.php index 8d78213d7c..967b27e70c 100644 --- a/plugins/QnA/lib/qnaanswerform.php +++ b/plugins/QnA/lib/qnanewanswerform.php @@ -44,7 +44,7 @@ if (!defined('STATUSNET')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @link http://status.net/ */ -class QnaanswerForm extends Form +class QnanewanswerForm extends Form { protected $question; diff --git a/plugins/QnA/lib/qnashowanswerform.php b/plugins/QnA/lib/qnashowanswerform.php new file mode 100644 index 0000000000..54f3f8fcac --- /dev/null +++ b/plugins/QnA/lib/qnashowanswerform.php @@ -0,0 +1,181 @@ +. + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/form.php'; + +/** + * Form for showing / revising an answer + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + */ +class QnashowanswerForm extends Form +{ + /** + * The answer to revise + */ + var $answer = null; + + /** + * The question this is an answer to + */ + var $question = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param QnA_Answer $answer answer to revise + */ + function __construct($out = null, $answer = null) + { + parent::__construct($out); + + $this->answer = $answer; + $this->question = $answer->getQuestion(); + } + + /** + * ID of the form + * + * @return int ID of the form + */ + function id() + { + return 'revise-' . $this->answer->id; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + function action() + { + return common_local_url('qnareviseanswer'); + } + + /** + * Include a session token for CSRF protection + * + * @return void + */ + function sessionToken() + { + $this->out->hidden( + 'token', + common_session_token() + ); + } + + /** + * Legend of the Form + * + * @return void + */ + function formLegend() + { + // TRANS: Form legend for revising the answer. + $this->out->element('legend', null, _('Revise your answer')); + } + + /** + * Data elements + * + * @return void + */ + function formData() + { + $this->out->hidden( + 'id', + 'revise-' . $this->answer->id + ); + $this->out->raw($this->answer->asHTML()); + } + + /** + * Action elements + * + * @return void + */ + function formActions() + { + $user = common_current_user(); + if (empty($user)) { + return; + } + + if (empty($this->question->closed)) { + if ($user->id == $this->question->profile_id) { + common_debug("I am the question asker!"); + if (empty($this->answer->best)) { + $this->out->submit( + 'best', + // TRANS: Button text for marking an answer as "best" + _m('BUTTON', 'Best'), + 'submit', + null, + // TRANS: Title for button text marking an answer as "best" + _('Mark as best answer') + ); + + } + } + if ($user->id == $this->answer->profile_id) { + $this->out->submit( + 'revise', + // TRANS: Button text for revising an answer + _m('BUTTON', 'Revise'), + 'submit', + null, + // TRANS: Title for button text for revising an answer + _('Revise your answer') + ); + } + } + } + + /** + * Class of the form. + * + * @return string the form's class + */ + function formClass() + { + return 'form_revise ajax'; + } +} From e6fd3fa0383dfaa914d2e55965cd4676f56206c4 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 12:02:33 -0700 Subject: [PATCH 05/20] QnA: Unify answer forms --- plugins/QnA/QnAPlugin.php | 2 +- plugins/QnA/lib/qnanewanswerform.php | 4 ++-- plugins/QnA/lib/qnareviseanswerform.php | 8 +++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index 08a73031a5..a586d89b40 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -391,7 +391,7 @@ class QnAPlugin extends MicroAppPlugin // Don't prompt user for an answer if the question is closed or // the current user posed the question in the first place if (empty($question->closed)) { - if (!empty($user) && ($user->id != $question->profile_id)) { + if (!empty($user)) { $profile = $user->getProfile(); $answer = $question->getAnswer($profile); if (!$answer) { diff --git a/plugins/QnA/lib/qnanewanswerform.php b/plugins/QnA/lib/qnanewanswerform.php index 967b27e70c..34e73def0a 100644 --- a/plugins/QnA/lib/qnanewanswerform.php +++ b/plugins/QnA/lib/qnanewanswerform.php @@ -103,9 +103,9 @@ class QnanewanswerForm extends Form $out = $this->out; $id = "question-" . $question->id; - $out->element('p', 'answer', $question->title); + $out->element('p', 'answer', 'Your answer'); $out->hidden('id', $id); - $out->element('input', array('type' => 'text', 'name' => 'answer')); + $out->textarea('answer', 'answer'); } /** diff --git a/plugins/QnA/lib/qnareviseanswerform.php b/plugins/QnA/lib/qnareviseanswerform.php index f9ebae132c..280dd74cd9 100644 --- a/plugins/QnA/lib/qnareviseanswerform.php +++ b/plugins/QnA/lib/qnareviseanswerform.php @@ -101,13 +101,11 @@ class QnareviseanswerForm extends Form */ function formData() { - $out = $this->out; - - $out->element('p', 'Your answer to:', $this->question->title); - + $out = $this->out; + $out->element('p', 'revise-answer', 'Your answer'); $id = "answer-" . $this->answer->id; $out->hidden('id', $id); - $out->textarea('answer', 'You said:', $this->answer->content); + $out->textarea('answer', 'answer', $this->answer->content); } /** From 82a357947db13f5d8526af2341108855872ae1be Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 4 Apr 2011 16:24:43 -0400 Subject: [PATCH 06/20] add force_scope flag to User_group --- classes/User_group.php | 1 + classes/statusnet.ini | 1 + 2 files changed, 2 insertions(+) diff --git a/classes/User_group.php b/classes/User_group.php index 8587f15771..a671464d67 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -28,6 +28,7 @@ class User_group extends Memcached_DataObject public $uri; // varchar(255) unique_key public $mainpage; // varchar(255) public $join_policy; // tinyint + public $force_scope; // tinyint /* Static get */ function staticGet($k,$v=NULL) { return DB_DataObject::staticGet('User_group',$k,$v); } diff --git a/classes/statusnet.ini b/classes/statusnet.ini index b9ffb28bc7..78e07b0407 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -625,6 +625,7 @@ modified = 384 uri = 2 mainpage = 2 join_policy = 1 +force_scope = 1 [user_group__keys] id = N From b41c62a27c6ef8039fb0853a6cf0663de77b1e05 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 4 Apr 2011 16:58:52 -0400 Subject: [PATCH 07/20] single flag for private groups --- actions/editgroup.php | 11 ++++++++++- actions/newgroup.php | 11 ++++++++++- classes/User_group.php | 13 +++++++++++++ lib/groupeditform.php | 15 ++++----------- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/actions/editgroup.php b/actions/editgroup.php index f5bada04fc..e46b1481d0 100644 --- a/actions/editgroup.php +++ b/actions/editgroup.php @@ -185,7 +185,15 @@ class EditgroupAction extends GroupDesignAction $description = $this->trimmed('description'); $location = $this->trimmed('location'); $aliasstring = $this->trimmed('aliases'); - $join_policy = intval($this->arg('join_policy')); + $private = $this->boolean('private'); + + if ($private) { + $force_scope = 1; + $join_policy = User_group::JOIN_POLICY_MODERATE; + } else { + $force_scope = 0; + $join_policy = User_group::JOIN_POLICY_OPEN; + } if ($this->nicknameExists($nickname)) { // TRANS: Group edit form validation error. @@ -267,6 +275,7 @@ class EditgroupAction extends GroupDesignAction $this->group->location = $location; $this->group->mainpage = common_local_url('showgroup', array('nickname' => $nickname)); $this->group->join_policy = $join_policy; + $this->group->force_scope = $force_scope; $result = $this->group->update($orig); diff --git a/actions/newgroup.php b/actions/newgroup.php index 540a42b9ba..c54e24ed95 100644 --- a/actions/newgroup.php +++ b/actions/newgroup.php @@ -130,8 +130,8 @@ class NewgroupAction extends Action $homepage = $this->trimmed('homepage'); $description = $this->trimmed('description'); $location = $this->trimmed('location'); + $private = $this->boolean('private'); $aliasstring = $this->trimmed('aliases'); - $join_policy = intval($this->arg('join_policy')); if ($this->nicknameExists($nickname)) { // TRANS: Group create form validation error. @@ -203,6 +203,14 @@ class NewgroupAction extends Action } } + if ($private) { + $force_scope = 1; + $join_policy = User_group::JOIN_POLICY_MODERATE; + } else { + $force_scope = 0; + $join_policy = User_group::JOIN_POLICY_OPEN; + } + $cur = common_current_user(); // Checked in prepare() above @@ -217,6 +225,7 @@ class NewgroupAction extends Action 'aliases' => $aliases, 'userid' => $cur->id, 'join_policy' => $join_policy, + 'force_scope' => $force_scope, 'local' => true)); $this->group = $group; diff --git a/classes/User_group.php b/classes/User_group.php index a671464d67..f72cc57533 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -515,12 +515,19 @@ class User_group extends Memcached_DataObject $group->uri = $uri; $group->mainpage = $mainpage; $group->created = common_sql_now(); + if (isset($fields['join_policy'])) { $group->join_policy = intval($fields['join_policy']); } else { $group->join_policy = 0; } + if (isset($fields['force_scope'])) { + $group->force_scope = intval($fields['force_scope']); + } else { + $group->force_scope = 0; + } + if (Event::handle('StartGroupSave', array(&$group))) { $result = $group->insert(); @@ -644,4 +651,10 @@ class User_group extends Memcached_DataObject } parent::delete(); } + + function isPrivate() + { + return ($this->join_policy == self::JOIN_POLICY_MODERATE && + $this->force_scope == 1); + } } diff --git a/lib/groupeditform.php b/lib/groupeditform.php index bb0ab4f7fb..c1218ab354 100644 --- a/lib/groupeditform.php +++ b/lib/groupeditform.php @@ -202,17 +202,10 @@ class GroupEditForm extends Form $this->out->elementEnd('li'); } $this->out->elementStart('li'); - $this->out->dropdown('join_policy', - // TRANS: Dropdown fieldd label on group edit form. - _('Membership policy'), - // TRANS: Group membership policy option. - array(User_group::JOIN_POLICY_OPEN => _('Open to all'), - // TRANS: Group membership policy option. - User_group::JOIN_POLICY_MODERATE => _('Admin must approve all members')), - // TRANS: Dropdown field title on group edit form. - _('Whether admin approval is required to join this group.'), - false, - (empty($this->group->join_policy)) ? User_group::JOIN_POLICY_OPEN : $this->group->join_policy); + $this->out->checkbox('private', _('Private'), + ($this->out->arg('private')) ? $this->out->arg('private') : + ((!empty($this->group)) ? $this->group->isPrivate() : false), + _('New members must be approved by admin and all posts are forced to be private')); $this->out->elementEnd('li'); Event::handle('EndGroupEditFormData', array($this)); } From 5193afb8bf304071f4af79f165bc27c66ee33d7e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 14:33:20 -0700 Subject: [PATCH 08/20] QnA - Allow closing questions --- plugins/QnA/QnAPlugin.php | 41 ++-- plugins/QnA/actions/qnaclosequestion.php | 200 ++++++++++++++++++ plugins/QnA/classes/QnA_Question.php | 49 +---- plugins/QnA/lib/qnanewanswerform.php | 4 +- ...uestionform.php => qnanewquestionform.php} | 2 +- plugins/QnA/lib/qnashowanswerform.php | 6 +- plugins/QnA/lib/qnashowquestionform.php | 160 ++++++++++++++ 7 files changed, 395 insertions(+), 67 deletions(-) create mode 100644 plugins/QnA/actions/qnaclosequestion.php rename plugins/QnA/lib/{qnaquestionform.php => qnanewquestionform.php} (98%) create mode 100644 plugins/QnA/lib/qnashowquestionform.php diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index a586d89b40..cc70cb7aeb 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -81,18 +81,19 @@ class QnAPlugin extends MicroAppPlugin case 'QnanewquestionAction': case 'QnanewanswerAction': case 'QnashowquestionAction': + case 'QnaclosequestionAction': case 'QnashowanswerAction': case 'QnareviseanswerAction': case 'QnavoteAction': include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; return false; - case 'QnaquestionForm': - case 'QnashowanswerForm': + case 'QnanewquestionForm': + case 'QnashowquestionForm': case 'QnanewanswerForm': + case 'QnashowanswerForm': case 'QnareviseanswerForm': case 'QnavoteForm': - case 'AnswerNoticeListItem': include_once $dir . '/lib/' . strtolower($cls).'.php'; break; case 'QnA_Question': @@ -122,6 +123,10 @@ class QnAPlugin extends MicroAppPlugin 'main/qna/newquestion', array('action' => 'qnanewquestion') ); + $m->connect( + 'answer/qna/closequestion', + array('action' => 'qnaclosequestion') + ); $m->connect( 'main/qna/newanswer', array('action' => 'qnanewanswer') @@ -384,23 +389,19 @@ class QnAPlugin extends MicroAppPlugin $question = QnA_Question::getByNotice($notice); if (!empty($question)) { - - $short = $this->shorten($question->description, $notice); - $out->raw($short); - - // Don't prompt user for an answer if the question is closed or - // the current user posed the question in the first place - if (empty($question->closed)) { - if (!empty($user)) { - $profile = $user->getProfile(); - $answer = $question->getAnswer($profile); - if (!$answer) { - $form = new QnanewanswerForm($question, $out); - $form->show(); - } - } + if (empty($user)) { + $form = new QnashowquestionForm($out, $question); + $form->show(); } else { - $out->element('span', 'closed', _m('This question is closed.')); + $profile = $user->getProfile(); + $answer = $question->getAnswer($profile); + if (empty($answer)) { + $form = new QnanewanswerForm($out, $question); + $form->show(); + } else { + $form = new QnashowquestionForm($out, $question); + $form->show(); + } } } else { $out->text(_m('Question data is missing.')); @@ -465,7 +466,7 @@ class QnAPlugin extends MicroAppPlugin function entryForm($out) { - return new QnaquestionForm($out); + return new QnanewquestionForm($out); } /** diff --git a/plugins/QnA/actions/qnaclosequestion.php b/plugins/QnA/actions/qnaclosequestion.php new file mode 100644 index 0000000000..28d4e547b4 --- /dev/null +++ b/plugins/QnA/actions/qnaclosequestion.php @@ -0,0 +1,200 @@ +. + * + * @category QnA + * @package StatusNet + * @author Zach Copley + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * Close a question to new answers + * + * @category QnA + * @package StatusNet + * @author Zach Copley + * @copyright 2010 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ +class QnaclosequestionAction extends Action +{ + protected $user = null; + protected $error = null; + protected $complete = null; + + protected $question = null; + protected $answer = null; + + /** + * Returns the title of the action + * + * @return string Action title + */ + function title() + { + // TRANS: Page title for close a question + return _m('Close question'); + } + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + function prepare($argarray) + { + parent::prepare($argarray); + if ($this->boolean('ajax')) { + StatusNet::setApi(true); + } + + $this->user = common_current_user(); + + if (empty($this->user)) { + // TRANS: Client exception thrown trying to close a question when not logged in + throw new ClientException( + _m("You must be logged in to close a question."), + 403 + ); + } + + if ($this->isPost()) { + $this->checkSessionToken(); + } + + $id = substr($this->trimmed('id'), 9); + $this->question = QnA_Question::staticGet('id', $id); + if (empty($this->question)) { + // TRANS: Client exception thrown trying to respond to a non-existing question. + throw new ClientException(_m('Invalid or missing question.'), 404); + } + + return true; + } + + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return void + */ + function handle($argarray=null) + { + parent::handle($argarray); + + if ($this->isPost()) { + $this->closeQuestion(); + } else { + $this->showPage(); + } + + return; + } + + /** + * Close a question + * + * @return void + */ + function closeQuestion() + { + + $user = common_current_user(); + + try { + + if ($user->id != $this->question->profile_id) { + throw new Exception(_m('You didn\'t ask this question.')); + } + + $orig = clone($this->question); + $this->question->closed = 1; + $this->question->update($orig); + + } catch (ClientException $ce) { + $this->error = $ce->getMessage(); + $this->showPage(); + return; + } + + if ($this->boolean('ajax')) { + header('Content-Type: text/xml;charset=utf-8'); + $this->xw->startDocument('1.0', 'UTF-8'); + $this->elementStart('html'); + $this->elementStart('head'); + // TRANS: Page title after sending an answer. + $this->element('title', null, _m('Answers')); + $this->elementEnd('head'); + $this->elementStart('body'); + $form = new QnashowquestionForm($this, $this->question); + $form->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect($this->question->bestUrl(), 303); + } + } + + /** + * Show the close question form + * + * @return void + */ + function showContent() + { + if (!empty($this->error)) { + $this->element('p', 'error', $this->error); + } + + // blar + } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + function isReadOnly($args) + { + if ($_SERVER['REQUEST_METHOD'] == 'GET' || + $_SERVER['REQUEST_METHOD'] == 'HEAD') { + return true; + } else { + return false; + } + } +} diff --git a/plugins/QnA/classes/QnA_Question.php b/plugins/QnA/classes/QnA_Question.php index 0446128ea0..93d45c56c8 100644 --- a/plugins/QnA/classes/QnA_Question.php +++ b/plugins/QnA/classes/QnA_Question.php @@ -201,66 +201,31 @@ class QnA_Question extends Managed_DataObject function asHTML() { - return self::toHTML( - $this->getProfile(), - $this, - $this->getAnswers() - ); + return self::toHTML($this->getProfile(), $this); } function asString() { - return self::toString( - $this->getProfile(), - $this, - $this->getAnswers() - ); + return self::toString($this->getProfile(), $this); } - static function toHTML($profile, $question, $answer) + static function toHTML($profile, $question) { $notice = $question->getNotice(); - $fmt = '

'; - $fmt .= '%2$s'; - $fmt .= '%3$s'; - $fmt .= 'asked by %5$s'; - $fmt .= '

'; + $fmt = '%s'; $q = sprintf( $fmt, - htmlspecialchars($notice->bestUrl()), - htmlspecialchars($question->title), - htmlspecialchars($question->description), - htmlspecialchars($profile->profileurl), - htmlspecialchars($profile->getBestName()) + htmlspecialchars($question->description) ); - $ans = array(); - - $ans[] = '
'; - - while($answer->fetch()) { - $ans[] = $answer->asHTML(); - } - - $ans[] .= '
'; - - return $q . implode($ans); + return $q; } static function toString($profile, $question, $answers) { - $fmt = _m( - '%1$s asked the question "%2$s": %3$s' - ); - - return sprintf( - $fmt, - htmlspecialchars($profile->getBestName()), - htmlspecialchars($question->title), - htmlspecialchars($question->description) - ); + return sprintf(htmlspecialchars($question->description)); } /** diff --git a/plugins/QnA/lib/qnanewanswerform.php b/plugins/QnA/lib/qnanewanswerform.php index 34e73def0a..db4d1dfafc 100644 --- a/plugins/QnA/lib/qnanewanswerform.php +++ b/plugins/QnA/lib/qnanewanswerform.php @@ -56,7 +56,7 @@ class QnanewanswerForm extends Form * * @return void */ - function __construct(QnA_Question $question, HTMLOutputter $out) + function __construct(HTMLOutputter $out, QnA_Question $question) { parent::__construct($out); $this->question = $question; @@ -103,6 +103,8 @@ class QnanewanswerForm extends Form $out = $this->out; $id = "question-" . $question->id; + $out->raw($this->question->asHTML()); + $out->element('p', 'answer', 'Your answer'); $out->hidden('id', $id); $out->textarea('answer', 'answer'); diff --git a/plugins/QnA/lib/qnaquestionform.php b/plugins/QnA/lib/qnanewquestionform.php similarity index 98% rename from plugins/QnA/lib/qnaquestionform.php rename to plugins/QnA/lib/qnanewquestionform.php index 9d0c2aad59..114e6199a1 100644 --- a/plugins/QnA/lib/qnaquestionform.php +++ b/plugins/QnA/lib/qnanewquestionform.php @@ -44,7 +44,7 @@ if (!defined('STATUSNET')) { * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 * @link http://status.net/ */ -class QnaquestionForm extends Form +class QnanewquestionForm extends Form { protected $title; protected $description; diff --git a/plugins/QnA/lib/qnashowanswerform.php b/plugins/QnA/lib/qnashowanswerform.php index 54f3f8fcac..5ec63e1096 100644 --- a/plugins/QnA/lib/qnashowanswerform.php +++ b/plugins/QnA/lib/qnashowanswerform.php @@ -65,8 +65,8 @@ class QnashowanswerForm extends Form { parent::__construct($out); - $this->answer = $answer; - $this->question = $answer->getQuestion(); + $this->answer = $answer; + $this->question = $answer->getQuestion(); } /** @@ -124,6 +124,7 @@ class QnashowanswerForm extends Form 'id', 'revise-' . $this->answer->id ); + $this->out->raw($this->answer->asHTML()); } @@ -141,7 +142,6 @@ class QnashowanswerForm extends Form if (empty($this->question->closed)) { if ($user->id == $this->question->profile_id) { - common_debug("I am the question asker!"); if (empty($this->answer->best)) { $this->out->submit( 'best', diff --git a/plugins/QnA/lib/qnashowquestionform.php b/plugins/QnA/lib/qnashowquestionform.php new file mode 100644 index 0000000000..71e644e2dc --- /dev/null +++ b/plugins/QnA/lib/qnashowquestionform.php @@ -0,0 +1,160 @@ +. + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/form.php'; + +/** + * Form for showing a question + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + */ +class QnashowquestionForm extends Form +{ + /** + * The question to show + */ + var $question = null; + + /** + * Constructor + * + * @param HTMLOutputter $out output channel + * @param QnA_Question $question the question to show + */ + function __construct($out = null, $question = null) + { + parent::__construct($out); + $this->question = $question; + } + + /** + * ID of the form + * + * @return int ID of the form + */ + function id() + { + return 'question-' . $this->question->id; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + function action() + { + return common_local_url('qnaclosequestion'); + } + + /** + * Include a session token for CSRF protection + * + * @return void + */ + function sessionToken() + { + $this->out->hidden( + 'token', + common_session_token() + ); + } + + /** + * Legend of the Form + * + * @return void + */ + function formLegend() + { + // TRANS: Form legend for revising the answer. + $this->out->element('legend', null, _('Question')); + } + + /** + * Data elements + * + * @return void + */ + function formData() + { + $this->out->hidden( + 'id', + 'question-' . $this->question->id + ); + + $this->out->raw($this->question->asHTML()); + } + + /** + * Action elements + * + * @return void + */ + function formActions() + { + $user = common_current_user(); + if (empty($user)) { + return; + } + + if (empty($this->question->closed)) { + if ($user->id == $this->question->profile_id) { + $this->out->submit( + 'close', + // TRANS: Button text for closing a question + _m('BUTTON', 'Close'), + 'submit', + null, + // TRANS: Title for button text for closing a question + _('Close the question') + ); + } + } + } + + /** + * Class of the form. + * + * @return string the form's class + */ + function formClass() + { + return 'form_close ajax'; + } +} From 660f1cd6b884743c33eb27910e2f6a8c3038fb23 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 4 Apr 2011 17:33:42 -0400 Subject: [PATCH 09/20] Force group scope on notices sent to a private-only group For groups that require a private scope, we force every notice to be limited to group scope. Changed the group-discovery code so we only get groups once -- regardless if they were provided or not. --- classes/Notice.php | 80 +++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 2fac7b9e2c..edd1b02cff 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -433,6 +433,22 @@ class Notice extends Memcached_DataObject } } + // Force the scope for private groups + + if (!isset($groups)) { + $groups = self::groupsFromText($notice->content, $profile); + } + + foreach ($groups as $groupId) { + $group = User_group::staticGet('id', $groupId); + if (!empty($group)) { + if ($group->force_scope) { + $notice->scope |= Notice::GROUP_SCOPE; + break; + } + } + } + if (Event::handle('StartNoticeSave', array(&$notice))) { // XXX: some of these functions write to the DB @@ -496,11 +512,9 @@ class Notice extends Memcached_DataObject // Note: groups may save tags, so must be run after tags are saved // to avoid errors on duplicates. - if (isset($groups)) { - $notice->saveKnownGroups($groups); - } else { - $notice->saveGroups(); - } + // Note: groups should always be set. + + $notice->saveKnownGroups($groups); if (isset($urls)) { $notice->saveKnownUrls($urls); @@ -940,7 +954,15 @@ class Notice extends Memcached_DataObject common_log_db_error($gi, 'INSERT', __FILE__); } - // @fixme should we save the tags here or not? + // we automatically add a tag for every group name, too + + $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->nickname), + 'notice_id' => $this->id)); + + if (is_null($tag)) { + $this->saveTag($group->nickname); + } + $groups[] = clone($group); } else { common_log(LOG_ERR, "Local delivery to group id $id skipped, doesn't exist"); @@ -962,36 +984,19 @@ class Notice extends Memcached_DataObject return array(); } - $groups = array(); - - /* extract all !group */ - $count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/', - strtolower($this->content), - $match); - if (!$count) { - return $groups; - } - $profile = $this->getProfile(); + $groups = self::groupsFromText($this->content, $profile); + /* Add them to the database */ - foreach (array_unique($match[1]) as $nickname) { + foreach ($groups as $group) { /* XXX: remote groups. */ - $group = User_group::getForNickname($nickname, $profile); if (empty($group)) { continue; } - // we automatically add a tag for every group name, too - - $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($nickname), - 'notice_id' => $this->id)); - - if (is_null($tag)) { - $this->saveTag($nickname); - } if ($profile->isMember($group)) { @@ -2180,4 +2185,27 @@ class Notice extends Memcached_DataObject return true; } + + static function groupsFromText($text, $profile) + { + $groups = array(); + + /* extract all !group */ + $count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/', + strtolower($text), + $match); + + if (!$count) { + return $groups; + } + + foreach (array_unique($match[1]) as $nickname) { + $group = User_group::getForNickname($nickname, $profile); + if (!empty($group) && $profile->isMember($group)) { + $groups[] = $group->id; + } + } + + return $groups; + } } From ddbd4801e067a4ec023f93a89bf28139b3c43f78 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 4 Apr 2011 17:44:23 -0400 Subject: [PATCH 10/20] include protected flag for users in JSON or XML --- lib/apiaction.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/apiaction.php b/lib/apiaction.php index 3b3ece17cd..1dfeea998a 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -202,6 +202,8 @@ class ApiAction extends Action { $twitter_user = array(); + $user = $profile->getUser(); + $twitter_user['id'] = intval($profile->id); $twitter_user['name'] = $profile->getBestName(); $twitter_user['screen_name'] = $profile->nickname; @@ -213,11 +215,10 @@ class ApiAction extends Action Avatar::defaultImage(AVATAR_STREAM_SIZE); $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null; - $twitter_user['protected'] = false; # not supported by StatusNet yet + $twitter_user['protected'] = ($user->private_stream) ? true : false; $twitter_user['followers_count'] = $profile->subscriberCount(); - $design = null; - $user = $profile->getUser(); + $design = null; // Note: some profiles don't have an associated user From 9a371658bd227309d88eb542b45dae9af5475aba Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 15:08:47 -0700 Subject: [PATCH 11/20] QnA - Rework output for notice stream --- plugins/QnA/QnAPlugin.php | 2 +- plugins/QnA/classes/QnA_Answer.php | 36 ++++++++++++++-------------- plugins/QnA/classes/QnA_Question.php | 12 ++++------ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index cc70cb7aeb..35811a309a 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -384,7 +384,7 @@ class QnAPlugin extends MicroAppPlugin $nli = new NoticeListItem($notice, $out); $nli->showNotice(); - $out->elementStart('div', array('class' => 'entry-content question-desciption')); + $out->elementStart('div', array('class' => 'entry-content question-description')); $question = QnA_Question::getByNotice($notice); diff --git a/plugins/QnA/classes/QnA_Answer.php b/plugins/QnA/classes/QnA_Answer.php index a2333be6dc..c8dcd43162 100644 --- a/plugins/QnA/classes/QnA_Answer.php +++ b/plugins/QnA/classes/QnA_Answer.php @@ -205,31 +205,31 @@ class QnA_Answer extends Managed_DataObject { $notice = $question->getNotice(); - $fmt = ''; + $out = new XMLStringer(); + $cls = array('qna_answer'); if (!empty($answer->best)) { - $fmt = '

'; - } else { - $fmt = '

'; + $cls[] = 'best'; } - $fmt .= 'answer by %3$s'; - $fmt .= '%4$s'; + $out->elementStart('p', array('class' => implode(' ', $cls))); + $out->elementStart('span', 'answer-content'); + $out->raw(QnAPlugin::shorten($answer->content, $notice)); + $out->elementEnd('span'); + if (!empty($answer->revisions)) { - $fmt .= '' - . $answer->revisions - . _m('revisions') - . ''; + $out->elementstart('span', 'answer-revisions'); + $out->text( + htmlspecialchars( + sprintf(_m('%s revisions'), $answer->revisions) + ) + ); + $out->elementEnd('span'); } - $fmt .= '

'; - return sprintf( - $fmt, - htmlspecialchars($notice->bestUrl()), - htmlspecialchars($profile->profileurl), - htmlspecialchars($profile->getBestName()), - htmlspecialchars($answer->content) - ); + $out->elementEnd('p'); + + return $out->getString(); } static function toString($profile, $question, $answer) diff --git a/plugins/QnA/classes/QnA_Question.php b/plugins/QnA/classes/QnA_Question.php index 93d45c56c8..3f1a33d33c 100644 --- a/plugins/QnA/classes/QnA_Question.php +++ b/plugins/QnA/classes/QnA_Question.php @@ -213,14 +213,12 @@ class QnA_Question extends Managed_DataObject { $notice = $question->getNotice(); - $fmt = '%s'; + $out = new XMLStringer(); + $out->elementStart('span', 'question_description'); + $out->raw(QnAPlugin::shorten($question->description, $notice)); + $out->elementEnd('span'); - $q = sprintf( - $fmt, - htmlspecialchars($question->description) - ); - - return $q; + return $out->getString(); } static function toString($profile, $question, $answers) From eaa70d337dad6a7caa5453bfebc3258d4e76c918 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 4 Apr 2011 18:13:28 -0400 Subject: [PATCH 12/20] force the reply to a notice to have the same scope --- classes/Notice.php | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index edd1b02cff..835d9af53e 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -342,6 +342,8 @@ class Notice extends Memcached_DataObject $notice->uri = $uri; $notice->url = $url; + $reply = null; + // Handle repeat case if (isset($repeat_of)) { @@ -379,18 +381,21 @@ class Notice extends Memcached_DataObject $notice->repeat_of = $repeat_of; } else { - $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final); - } + $reply = self::getReplyTo($reply_to, $profile_id, $source, $final); - if (!empty($notice->reply_to)) { - $reply = Notice::staticGet('id', $notice->reply_to); - if (!$reply->inScope($profile)) { - // TRANS: Client error displayed when trying to reply to a notice a the target has no access to. - // TRANS: %1$s is a user nickname, %2$d is a notice ID (number). - throw new ClientException(sprintf(_('%1$s has no access to notice %2$d.'), - $profile->nickname, $reply->id), 403); + if (!empty($reply)) { + + if (!$reply->inScope($profile)) { + // TRANS: Client error displayed when trying to reply to a notice a the target has no access to. + // TRANS: %1$s is a user nickname, %2$d is a notice ID (number). + throw new ClientException(sprintf(_('%1$s has no access to notice %2$d.'), + $profile->nickname, $reply->id), 403); + } + + $notice->reply_to = $reply->id; + $notice->conversation = $reply->conversation; + // Scope set below } - $notice->conversation = $reply->conversation; } if (!empty($lat) && !empty($lon)) { @@ -416,7 +421,11 @@ class Notice extends Memcached_DataObject } if (is_null($scope)) { // 0 is a valid value - $notice->scope = common_config('notice', 'defaultscope'); + if (!empty($reply)) { + $notice->scope = $reply->scope; + } else { + $notice->scope = common_config('notice', 'defaultscope'); + } } else { $notice->scope = $scope; } @@ -1529,7 +1538,7 @@ class Notice extends Memcached_DataObject if (!empty($reply_to)) { $reply_notice = Notice::staticGet('id', $reply_to); if (!empty($reply_notice)) { - return $reply_to; + return $reply_notice; } } @@ -1568,8 +1577,10 @@ class Notice extends Memcached_DataObject $last = $recipient->getCurrentNotice(); if (!empty($last)) { - return $last->id; + return $last; } + + return null; } static function maxContent() From 53c653038d4127626ab4506d578a80804ed65439 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 15:28:28 -0700 Subject: [PATCH 13/20] QnA - Don't output question-description if it's empty; Do output question-closed msg if question is closed --- plugins/QnA/classes/QnA_Question.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/plugins/QnA/classes/QnA_Question.php b/plugins/QnA/classes/QnA_Question.php index 3f1a33d33c..ed1757e989 100644 --- a/plugins/QnA/classes/QnA_Question.php +++ b/plugins/QnA/classes/QnA_Question.php @@ -214,9 +214,18 @@ class QnA_Question extends Managed_DataObject $notice = $question->getNotice(); $out = new XMLStringer(); - $out->elementStart('span', 'question_description'); - $out->raw(QnAPlugin::shorten($question->description, $notice)); - $out->elementEnd('span'); + + if (!empty($question->description)) { + $out->elementStart('span', 'question-description'); + $out->raw(QnAPlugin::shorten($question->description, $notice)); + $out->elementEnd('span'); + } + + if (!empty($question->closed)) { + $out->elementStart('span', 'question-closed'); + $out->text(_m('This question is closed.')); + $out->elementEnd('span'); + } return $out->getString(); } From 92156317b6410b64f7667e9b14b8ebde91db151d Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 17:04:13 -0700 Subject: [PATCH 14/20] QnA - truncate long question titles in notice content --- plugins/QnA/QnAPlugin.php | 4 ++-- plugins/QnA/actions/qnanewquestion.php | 1 + plugins/QnA/classes/QnA_Question.php | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index 35811a309a..5b6c1eb91c 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -356,7 +356,7 @@ class QnAPlugin extends MicroAppPlugin * @param Notice $notice * @param HTMLOutputter $out */ - + function showNotice($notice, $out) { switch ($notice->object_type) { @@ -415,7 +415,7 @@ class QnAPlugin extends MicroAppPlugin function showNoticeAnswer($notice, $out) { $user = common_current_user(); - + $answer = QnA_Answer::getByNotice($notice); $question = $answer->getQuestion(); diff --git a/plugins/QnA/actions/qnanewquestion.php b/plugins/QnA/actions/qnanewquestion.php index 8682f8dd47..561b11575a 100644 --- a/plugins/QnA/actions/qnanewquestion.php +++ b/plugins/QnA/actions/qnanewquestion.php @@ -88,6 +88,7 @@ class QnanewquestionAction extends Action } $this->title = $this->trimmed('title'); + common_debug("TITLE = " . $this->title); $this->description = $this->trimmed('description'); return true; diff --git a/plugins/QnA/classes/QnA_Question.php b/plugins/QnA/classes/QnA_Question.php index ed1757e989..7055876369 100644 --- a/plugins/QnA/classes/QnA_Question.php +++ b/plugins/QnA/classes/QnA_Question.php @@ -273,15 +273,17 @@ class QnA_Question extends Managed_DataObject common_log(LOG_DEBUG, "Saving question: $q->id $q->uri"); $q->insert(); - // TRANS: Notice content creating a question. - // TRANS: %1$s is the title of the question, %2$s is a link to the question. - $content = sprintf( - _m('question: %1$s %2$s'), - $title, - $q->uri - ); + if (Notice::contentTooLong($q->title . ' ' . $q->uri)) { + $max = Notice::maxContent(); + $uriLen = mb_strlen($q->uri); + $targetLen = $max - ($uriLen + 15); + $title = mb_substr($q->title, 0, $targetLen) . '…'; - $link = '' . htmlspecialchars($title) . ''; + } + + $content = $title . ' ' . $q->uri; + + $link = '' . htmlspecialchars($q->title) . ''; // TRANS: Rendered version of the notice content creating a question. // TRANS: %s a link to the question as link description. $rendered = sprintf(_m('Question: %s'), $link); From a3ba11a8ac15449cedb99f0eb847bbd2301a7012 Mon Sep 17 00:00:00 2001 From: Samantha Doherty Date: Mon, 4 Apr 2011 20:29:30 -0400 Subject: [PATCH 15/20] Style for QnA plugin. --- theme/neo/css/display.css | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/theme/neo/css/display.css b/theme/neo/css/display.css index 5128ed8bc5..1932a4a1a2 100644 --- a/theme/neo/css/display.css +++ b/theme/neo/css/display.css @@ -1246,4 +1246,60 @@ table.profile_list tr.alt { filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FB6104', endColorstr='#fc8035',GradientType=0 ); } +/* QnA specific styles */ + +#content .question .entry-title { + min-height: 1px; +} + +.question div.question-description { + font-size: 1em; + line-height: 1.36em; + opacity: 1; +} + +.question fieldset { + margin: 0px; +} + +.question fieldset legend { + display: none; +} + +.question p.answer { + margin-top: 4px; + margin-bottom: 4px; + font-style: italic; +} + +.question label[for=answer] { + display: none; +} + +.question textarea { + width: 100%; + height: 42px; + padding: 6px 10px 18px 10px; + border: 1px solid #a6a6a6; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2); + font-size: 1.2em; + margin-bottom: 10px; +} + +.question #answer-form input.submit { + height: auto; + padding: 0px 10px; + margin-left: 0px; + margin-bottom: 10px; + color:#fff; + font-weight: bold; + text-transform: uppercase; + font-size: 1.1em; +} + }/*end of @media screen, projection, tv*/ From 559a688e4256918cb405fbf82a2bc266ea9ac35b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 17:31:38 -0700 Subject: [PATCH 16/20] QnA - Add in stub js --- plugins/QnA/QnAPlugin.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index 5b6c1eb91c..38fd85e26a 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -496,7 +496,8 @@ class QnAPlugin extends MicroAppPlugin function onEndShowScripts($action) { - // XXX maybe some cool shiz here + $action->script($this->path('js/qna.js')); + return true; } function onEndShowStyles($action) From 84b328450fda99aa840d8a1c424be86ded2ebff5 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 18:06:29 -0700 Subject: [PATCH 17/20] QnA - Whoops - this actually removes the unused function mentioned in the last commit --- plugins/QnA/QnAPlugin.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php index 38fd85e26a..fe42975065 100644 --- a/plugins/QnA/QnAPlugin.php +++ b/plugins/QnA/QnAPlugin.php @@ -279,24 +279,6 @@ class QnAPlugin extends MicroAppPlugin return $obj; } - /** - * Change the verb on Answer notices - * - * @param Notice $notice - * - * @return ActivityObject - */ - - function onEndNoticeAsActivity($notice, &$act) { - switch ($notice->object_type) { - case Answer::NORMAL: - case Answer::ANONYMOUS: - $act->verb = $notice->object_type; - break; - } - return true; - } - /** * Output our CSS class for QnA notice list elements * From a9856e6550e27bb0673332adc444402e5c322e0e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 4 Apr 2011 22:16:38 -0400 Subject: [PATCH 18/20] Direct events to users or groups --- plugins/Event/eventform.php | 7 +++++++ plugins/Event/newevent.php | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/Event/eventform.php b/plugins/Event/eventform.php index 31fecb1278..dfdb65a30d 100644 --- a/plugins/Event/eventform.php +++ b/plugins/Event/eventform.php @@ -147,6 +147,13 @@ class EventForm extends Form _m('Description of the event.')); $this->unli(); + $this->li(); + $toWidget = new ToSelector($this->out, + common_current_user(), + null); + $toWidget->show(); + $this->unli(); + $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); } diff --git a/plugins/Event/newevent.php b/plugins/Event/newevent.php index 082f12bb4b..93baa37b6b 100644 --- a/plugins/Event/newevent.php +++ b/plugins/Event/newevent.php @@ -190,6 +190,12 @@ class NeweventAction extends Action throw new ClientException(_m('Event must have an end time.')); } + $options = array(); + + // Does the heavy-lifting for getting "To:" information + + ToSelector::fillOptions($this, $options); + $profile = $this->user->getProfile(); $saved = Happening::saveNew($profile, @@ -198,7 +204,8 @@ class NeweventAction extends Action $this->title, $this->location, $this->description, - $this->url); + $this->url, + $options); $event = Happening::fromNotice($saved); From 797cd9ccd4bd9d90d37411583d52bf8cad45a556 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 4 Apr 2011 22:34:47 -0400 Subject: [PATCH 19/20] fix scope for replies to group-private notices --- classes/Notice.php | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index 835d9af53e..a4f530c44f 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -342,6 +342,12 @@ class Notice extends Memcached_DataObject $notice->uri = $uri; $notice->url = $url; + // Get the groups here so we can figure out replies and such + + if (!isset($groups)) { + $groups = self::groupsFromText($notice->content, $profile); + } + $reply = null; // Handle repeat case @@ -394,6 +400,20 @@ class Notice extends Memcached_DataObject $notice->reply_to = $reply->id; $notice->conversation = $reply->conversation; + + // If the original is private to a group, and notice has no group specified, + // make it to the same group(s) + + if (empty($groups) && ($reply->scope | Notice::GROUP_SCOPE)) { + $groups = array(); + $replyGroups = $reply->getGroups(); + foreach ($replyGroups as $group) { + if ($profile->isMember($group)) { + $groups[] = $group->id; + } + } + } + // Scope set below } } @@ -444,10 +464,6 @@ class Notice extends Memcached_DataObject // Force the scope for private groups - if (!isset($groups)) { - $groups = self::groupsFromText($notice->content, $profile); - } - foreach ($groups as $groupId) { $group = User_group::staticGet('id', $groupId); if (!empty($group)) { From 338a75e12b1abd406d42e183d1d9b4eb0053ce5b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 4 Apr 2011 21:36:42 -0700 Subject: [PATCH 20/20] QnA - JavaScript to hide close and best buttons when clicked --- plugins/QnA/js/qna.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 plugins/QnA/js/qna.js diff --git a/plugins/QnA/js/qna.js b/plugins/QnA/js/qna.js new file mode 100644 index 0000000000..82f9a24b1b --- /dev/null +++ b/plugins/QnA/js/qna.js @@ -0,0 +1,27 @@ + +var QnA = { + + // hide all the 'close' and 'best' buttons for this question + + // @fixme: Should use ID + close: function(closeButt) { + $(closeButt) + .closest('li.hentry.notice.question') + .find('input[name=best],[name=close]') + .hide(); + }, + + init: function() { + var that = this; + $('input[name=close]').live('click', function() { + that.close(this); + }); + $('input[name=best]').live('click', function() { + that.close(this); + }); + } +}; + +$(document).ready(function() { + QnA.init(); +});