Example #1
0
 /**
  * Prepare array of data for the answer,
  * then create Answer object from it and
  * save. It will also fire onBeforeNewAnswer
  * and onNewAnswer events
  *
  * @throws AnswerParserException
  *
  * @return object $this
  */
 protected function makeAnswer()
 {
     $username = $this->SubmittedAnswer->getUserObject()->getDisplayName();
     /**
      * Must pass array('drop-proprietary-attributes' => false)
      * otherwise tidy removes rel="code"
      */
     $aEditorConfig = $this->Registry->Ini->getSection('EDITOR');
     $tidyConfig = $aEditorConfig['ENABLE_CODE_EDITOR'] ? array('drop-proprietary-attributes' => false) : null;
     $Body = $this->SubmittedAnswer->getBody()->tidy($tidyConfig)->safeHtml()->asHtml();
     $htmlBody = HTMLStringParser::factory($Body)->parseCodeTags()->linkify()->importCDATA()->setNofollow()->valueOf();
     d('after HTMLStringParser: ' . $htmlBody);
     $username = $this->SubmittedAnswer->getUserObject()->getDisplayName();
     $uid = $this->SubmittedAnswer->getUserObject()->getUid();
     $qid = $this->SubmittedAnswer->getQid();
     $hash = hash('md5', \mb_strtolower($htmlBody . $qid));
     /**
      *
      * We need to copy the title
      * here too because Answer by itself does not have own
      * title but we need a title when displaying links
      * to answer on profile pages
      *
      * @todo later can also parse for smilies here
      *
      */
     $this->checkForDuplicate($hash);
     $aData = array('_id' => $this->Registry->Resource->create('ANSWER'), 'i_qid' => $qid, 'i_uid' => $uid, 'i_quid' => $this->Question->getOwnerId(), 'title' => $this->Question->getTitle(), 'hash' => $hash, 'username' => $username, 'ulink' => '<a href="' . $this->SubmittedAnswer->getUserObject()->getProfileUrl() . '">' . $username . '</a>', 'avtr' => $this->SubmittedAnswer->getUserObject()->getAvatarSrc(), 'i_words' => $Body->asPlainText()->getWordsCount(), 'i_up' => 0, 'i_down' => 0, 'i_votes' => 0, 'i_cat' => $this->Question->getCategoryId(), 'b' => $htmlBody, 'i_ts' => time(), 'i_lm_ts' => time(), 'hts' => date('F j, Y g:i a T'), 'v_s' => 's', 'accepted' => false, 'ip' => $this->SubmittedAnswer->getIP(), 'app' => 'web');
     /**
      * Submitted answer object may provide
      * extra elements to be added to aData array
      * This is usually useful for parsing answers that
      * came from external API
      *
      * as well as adding 'credit' div
      */
     $aExtraData = $this->SubmittedAnswer->getExtraData();
     d('$aExtraData: ' . print_r($aExtraData, 1));
     if (!empty($aExtraData)) {
         $aData = array_merge($aData, $aExtraData);
     }
     d('$aData: ' . print_r($aData, 1));
     $this->Answer = new Answer($this->Registry, $aData);
     /**
      * Post onBeforeNewQuestion event
      * and watch for filter either cancelling the event
      * or throwing FilterException (prefferred way because
      * a specific error message can be passed in FilterException
      * this way)
      *
      * In either case we throw QuestionParserException
      * Controller that handles the question form should be ready
      * to handle this exception and set the form error using
      * message from exception. This way the error will be shown to
      * the user right on the question form while question form's data
      * is preserved in form.
      */
     try {
         $oNotification = $this->Registry->Dispatcher->post($this->Answer, 'onBeforeNewAnswer');
         if ($oNotification->isNotificationCancelled()) {
             throw new AnswerParserException('Sorry, we are unable to process your answer at this time.');
         }
     } catch (FilterException $e) {
         e('Got filter exteption: ' . $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage() . ' ' . $e->getTraceAsString());
         throw new AnswerParserException($e->getMessage());
     }
     /**
      * Do ensureIndexes() now and not before we are sure that we even going
      * to add a new question.
      */
     $this->ensureIndexes();
     $this->Answer->insert();
     d('cp');
     $this->Registry->Dispatcher->post($this->Answer, 'onNewAnswer', array('question' => $this->Question));
     d('cp');
     /**
      * Reuse $uid since we already resolved it here,
      * so no need to go through the same
      * $this->SubmittedAnswer->getUserObject()->getUid() again
      */
     $this->addUserTags($uid);
     d('cp');
     return $this;
 }
 /**
  * Prepares data for the question object,
  * creates the $this->Question object
  * and saves data to QUESTIONS collection
  *
  * @return object $this
  *
  * @throws QuestionParserException in case a filter (which is an observer)
  * either throws a FilterException (or sub-class of it) OR just cancells event
  *
  */
 protected function makeQuestion()
 {
     $oTitle = $this->Submitted->getTitle()->htmlentities()->trim();
     $username = $this->Submitted->getUserObject()->getDisplayName();
     $aTags = $this->Submitted->getTagsArray();
     /**
      * Must pass array('drop-proprietary-attributes' => false)
      * otherwise tidy removes rel="code"
      */
     $aEditorConfig = $this->Registry->Ini->getSection('EDITOR');
     $tidyConfig = $aEditorConfig['ENABLE_CODE_EDITOR'] ? array('drop-proprietary-attributes' => false) : null;
     $Body = $this->Submitted->getBody()->tidy($tidyConfig)->safeHtml()->asHtml();
     /**
      *
      * Now body is in html but we still need to run
      * it through HTMLStringParser string in order
      * to make clickable links and to
      * make sure all links are nofollow
      *
      */
     $htmlBody = HTMLStringParser::factory($Body)->parseCodeTags()->linkify()->importCDATA()->setNofollow()->hilightWords($aTags)->valueOf();
     d('after HTMLStringParser: ' . $htmlBody);
     $uid = $this->Submitted->getUserObject()->getUid();
     $hash = hash('md5', strtolower($htmlBody . json_encode($aTags)));
     /**
      * @todo can parse forMakrdown now but ideally
      * parseMarkdown() would be done inside Utf8string
      * as well as parseSmilies
      *
      * @todo later can also parse for smilies here
      *
      */
     $this->checkForDuplicate($uid, $hash);
     $username = $this->Submitted->getUserObject()->getDisplayName();
     $time = time();
     /**
      *
      * @var array
      */
     $aData = array('_id' => $this->Registry->Resource->create('QUESTION'), 'title' => $oTitle->valueOf(), 'b' => $htmlBody, 'hash' => $hash, 'intro' => $this->Submitted->getBody()->asPlainText()->truncate(150)->valueOf(), 'url' => $this->Submitted->getTitle()->toASCII()->makeLinkTitle()->valueOf(), 'i_words' => $this->Submitted->getBody()->asPlainText()->getWordsCount(), 'i_uid' => $uid, 'username' => $username, 'ulink' => '<a href="' . $this->Submitted->getUserObject()->getProfileUrl() . '">' . $username . '</a>', 'avtr' => $this->Submitted->getUserObject()->getAvatarSrc(), 'i_up' => 0, 'i_down' => 0, 'i_votes' => 0, 'i_favs' => 0, 'i_views' => 0, 'i_cat' => $this->Submitted->getCategoryId(), 'a_tags' => $aTags, 'a_title' => TitleTokenizer::factory($oTitle)->getArrayCopy(), 'status' => 'unans', 'tags_html' => \tplQtags::loop($aTags, false), 'credits' => '', 'i_ts' => $time, 'hts' => date('F j, Y g:i a T'), 'i_lm_ts' => $time, 'i_ans' => 0, 'ans_s' => 's', 'v_s' => 's', 'f_s' => 's', 'ip' => $this->Submitted->getIP(), 'app' => $this->Submitted->getApp(), 'app_id' => $this->Submitted->getAppId(), 'app_link' => $this->Submitted->getAppLink(), 'i_flwrs' => 1);
     /**
      * Submitted question object may provide
      * extra elements to be added to aData array
      * This is usually useful for parsing questions that
      * came from external API, in which case the answered/unanswred
      * status as well as number of answers is already known
      *
      * as well as adding 'credit' div
      */
     $aExtraData = $this->Submitted->getExtraData();
     d('$aExtraData: ' . print_r($aExtraData, 1));
     if (is_array($aExtraData) && !empty($aExtraData)) {
         $aData = array_merge($aData, $aExtraData);
     }
     $this->Question = new Question($this->Registry, $aData);
     /**
      * Post onBeforeNewQuestion event
      * and watch for filter either cancelling the event
      * or throwing FilterException (prefferred way because
      * a specific error message can be passed in FilterException
      * this way)
      *
      * In either case we throw QuestionParserException
      * Controller that handles the question form should be ready
      * to handle this exception and set the form error using
      * message from exception. This way the error will be shown to
      * the user right on the question form while question form's data
      * is preserved in form.
      *
      * Filter can also modify the data in Question before
      * it is saved. This is convenient, we can even set different
      * username, i_uid if we want to 'post as alias'
      */
     try {
         $oNotification = $this->Registry->Dispatcher->post($this->Question, 'onBeforeNewQuestion');
         if ($oNotification->isNotificationCancelled()) {
             throw new QuestionParserException('Sorry, we are unable to process your question at this time.');
         }
     } catch (FilterException $e) {
         e('Got filter exteption: ' . $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage() . ' ' . $e->getTraceAsString());
         throw new QuestionParserException($e->getMessage());
     }
     /**
      * Do ensureIndexes() now and not before we are sure that we even going
      * to add a new question.
      */
     $this->ensureIndexes();
     $this->Question->insert();
     $this->followQuestion();
     $this->Registry->Dispatcher->post($this->Question, 'onNewQuestion');
     return $this;
 }
 /**
  *
  * Set tags for this question
  * It will also update "a_edited" array
  * to record the retag action, records
  * user who retagged, and "Retag" as reason for edit
  * Will also update lastModified
  *
  * @param User $user object User who retagged this question
  * @param array $tags array of tags
  */
 public function retag(User $user, array $tags)
 {
     parent::offsetSet('a_tags', $tags);
     parent::offsetSet('tags_html', \tplQtags::loop($tags, false));
     $b = $this->offsetGet('b');
     d('b: ' . $b);
     $oHtmlParser = \Lampcms\String\HTMLStringParser::factory(Utf8String::factory($b, 'utf-8', true));
     $body = $oHtmlParser->unhilight()->hilightWords($tags)->valueOf();
     $this->offsetSet('b', $body);
     $this->setEdited($user, 'Retagged')->touch();
     return $this;
 }
Example #4
0
 /**
  *
  * Update the contents of body
  * with edited content
  * If this is a question do extra steps;
  * unhighlight (just in case that actual highlighed words
  * have been edited), then re-apply highlightWords()
  * just in case some of the new word that belong to
  * tags have been added
  *
  * @param string $body
  *
  * @return string html of new body
  *
  */
 protected function makeBody($body)
 {
     /**
      * Must pass array('drop-proprietary-attributes' => false)
      * otherwise tidy removes rel="code"
      */
     $aEditorConfig = $this->Registry->Ini->getSection('EDITOR');
     $tidyConfig = $aEditorConfig['ENABLE_CODE_EDITOR'] ? array('drop-proprietary-attributes' => false) : null;
     $this->Body = Utf8String::stringFactory($body)->tidy($tidyConfig)->safeHtml()->asHtml();
     $Body = HTMLStringParser::stringFactory($this->Body)->parseCodeTags()->linkify()->reload()->setNofollow();
     if ($this->Resource instanceof \Lampcms\Question) {
         $Body->unhilight()->hilightWords($this->Resource[QuestionSchema::TAGS_ARRAY]);
     }
     $this->plainTextBody = $this->Body->asPlainText();
     d('plain text body: ' . $this->plainTextBody);
     $htmlBody = $Body->valueOf();
     d('after HTMLStringParser: ' . $htmlBody);
     return $htmlBody;
 }
 /**
  * Prepares data for the question object,
  * creates the $this->Question object
  * and saves data to QUESTIONS collection
  *
  * @return object $this
  *
  * @throws QuestionParserException in case a filter (which is an observer)
  * either throws a FilterException (or sub-class of it) OR just cancels event
  *
  */
 protected function makeQuestion()
 {
     $Ini = $this->Registry->Ini;
     $oTitle = $this->Submitted->getTitle()->htmlentities()->trim();
     $username = $this->Submitted->getUserObject()->getDisplayName();
     $aTags = $this->Submitted->getTagsArray();
     /**
      * Must pass array('drop-proprietary-attributes' => false)
      * otherwise tidy removes rel="code"
      */
     $aEditorConfig = $Ini->getSection('EDITOR');
     $tidyConfig = $aEditorConfig['ENABLE_CODE_EDITOR'] ? array('drop-proprietary-attributes' => false) : null;
     $Body = $this->Submitted->getBody()->tidy($tidyConfig)->safeHtml()->asHtml();
     /**
      *
      * Now body is in html but we still need to run
      * it through HTMLStringParser string in order
      * to make clickable links and to
      * make sure all links are nofollow
      *
      */
     $HtmlDoc = HTMLStringParser::stringFactory($Body)->parseCodeTags()->linkify()->importCDATA()->setNofollow()->hilightWords($aTags)->parseImages();
     $aImages = $HtmlDoc->getImages();
     $htmlBody = $HtmlDoc->valueOf();
     d('after HTMLStringParser: ' . $htmlBody);
     $uid = $this->Submitted->getUserObject()->getUid();
     $hash = hash('md5', strtolower($htmlBody . json_encode($aTags)));
     /**
      * @todo can parse forMakrdown now but ideally
      *       parseMarkdown() would be done inside Utf8string
      *       as well as parseSmilies
      *
      * @todo later can also parse for smilies here
      *
      */
     $this->checkForDuplicate($uid, $hash);
     $Poster = $this->Submitted->getUserObject();
     $username = $Poster->getDisplayName();
     $time = time();
     /**
      * If NEW_POSTS_MODERATION in !config.ini is > 0 then
      * check if viewer requires new posts to be moderated
      */
     $resourceStatus = $Ini->NEW_POSTS_MODERATION > 0 && $Poster->isOnProbation() ? Schema::PENDING : Schema::POSTED;
     /**
      *
      * @var array
      */
     $aData = array(Schema::PRIMARY => $this->Registry->Resource->create('QUESTION'), Schema::TITLE => $oTitle->valueOf(), Schema::BODY => $htmlBody, Schema::BODY_HASH => $hash, Schema::INTRO => $this->Submitted->getBody()->asPlainText()->truncate(150)->valueOf(), Schema::URL => $this->Submitted->getTitle()->toASCII()->makeLinkTitle()->valueOf(), Schema::WORDS_COUNT => $this->Submitted->getBody()->asPlainText()->getWordsCount(), Schema::POSTER_ID => $uid, Schema::POSTER_USERNAME => $username, Schema::USER_PROFILE_URL => '<a href="' . $Poster->getProfileUrl() . '">' . $username . '</a>', Schema::AVATAR_URL => $Poster->getAvatarSrc(), Schema::UPVOTES_COUNT => 0, Schema::DOWNVOTES_COUNT => 0, Schema::VOTES_SCORE => 0, Schema::NUM_FAVORITES => 0, Schema::NUM_VIEWS => 0, Schema::CATEGORY_ID => $this->Submitted->getCategoryId(), Schema::TAGS_ARRAY => $aTags, Schema::TITLE_ARRAY => TitleTokenizer::factory($oTitle)->getArrayCopy(), Schema::STATUS => 'unans', Schema::TAGS_HTML => \tplQtags::loop($aTags, false), Schema::CREDITS => '', Schema::CREATED_TIMESTAMP => $time, Schema::TIME_STRING => date('F j, Y g:i a T'), Schema::LAST_MODIFIED_TIMESTAMP => $time, Schema::NUM_ANSWERS => 0, 'ans_s' => 's', 'v_s' => 's', 'f_s' => 's', Schema::IP_ADDRESS => $this->Submitted->getIP(), Schema::APP_NAME => $this->Submitted->getApp(), Schema::APP_ID => $this->Submitted->getAppId(), Schema::APP_LINK => $this->Submitted->getAppLink(), Schema::NUM_FOLLOWERS => 1, Schema::RESOURCE_STATUS_ID => $resourceStatus);
     if (!empty($aImages)) {
         $aData[Schema::UPLOADED_IMAGES] = $aImages;
     }
     /**
      * Submitted question object may provide
      * extra elements to be added to aData array
      * This is usually useful for parsing questions that
      * came from external API, in which case the answered/unanswred
      * status as well as number of answers is already known
      *
      * as well as adding 'credit' div
      */
     $aExtraData = $this->Submitted->getExtraData();
     d('$aExtraData: ', $aExtraData);
     if (\is_array($aExtraData) && !empty($aExtraData)) {
         $aData = array_merge($aData, $aExtraData);
     }
     $this->Question = new Question($this->Registry, $aData);
     /**
      * Post onBeforeNewQuestion event
      * and watch for filter either cancelling the event
      * or throwing FilterException (preferred way because
      * a specific error message can be passed in FilterException
      * this way)
      *
      * In either case we throw QuestionParserException
      * Controller that handles the question form should be ready
      * to handle this exception and set the form error using
      * message from exception. This way the error will be shown to
      * the user right on the question form while question form's data
      * is preserved in form.
      *
      * Filter can also modify the data in Question before
      * it is saved. This is convenient, we can even set different
      * username, i_uid if we want to 'post as alias'
      */
     try {
         $oNotification = $this->Registry->Dispatcher->post($this->Question, 'onBeforeNewQuestion');
         if ($oNotification->isNotificationCancelled()) {
             throw new QuestionParserException('@@Sorry, we are unable to process your question at this time@@.');
         }
     } catch (FilterException $e) {
         e('Got filter exception: ' . $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage() . ' ' . $e->getTraceAsString());
         throw new QuestionParserException($e->getMessage());
     }
     /**
      * Do ensureIndexes() now and not before we are sure that we even going
      * to add a new question.
      */
     $this->ensureIndexes();
     $this->Question->insert();
     $this->followQuestion();
     if ($resourceStatus === Schema::POSTED) {
         $this->updateCategory()->addTags()->addUnansweredTags()->addRelatedTags();
         $this->Registry->Dispatcher->post($this->Question, 'onCategoryUpdate');
         $this->Registry->Dispatcher->post($this->Question, 'onNewQuestion');
     } elseif ($resourceStatus === Schema::PENDING) {
         $this->Registry->Dispatcher->post($this->Question, 'onNewPendingQuestion');
     }
     return $this;
 }
 /**
  * Prepare array of data for the answer,
  * then create Answer object from it and
  * save. It will also fire onBeforeNewAnswer
  * and onNewAnswer events
  *
  * @throws AnswerParserException
  *
  * @return object $this
  */
 protected function makeAnswer()
 {
     /**
      * Must pass array('drop-proprietary-attributes' => false)
      * otherwise tidy removes rel="code"
      */
     $aEditorConfig = $this->Registry->Ini->getSection('EDITOR');
     $tidyConfig = $aEditorConfig['ENABLE_CODE_EDITOR'] ? array('drop-proprietary-attributes' => false) : null;
     $Body = $this->SubmittedAnswer->getBody()->tidy($tidyConfig)->safeHtml()->asHtml();
     $HtmlDoc = HTMLStringParser::stringFactory($Body)->parseCodeTags()->linkify()->importCDATA()->setNofollow()->parseImages();
     $aImages = $HtmlDoc->getImages();
     $htmlBody = $HtmlDoc->valueOf();
     d('after HTMLStringParser: ' . $htmlBody);
     $Poster = $this->SubmittedAnswer->getUserObject();
     $username = $Poster->getDisplayName();
     $uid = $Poster->getUid();
     $qid = $this->SubmittedAnswer->getQid();
     $hash = hash('md5', \mb_strtolower($htmlBody . $qid));
     /**
      *
      * We need to copy the title
      * here too because Answer by itself does not have own
      * title but we need a title when displaying links
      * to answer on profile pages
      *
      * @todo later can also parse for smilies here
      *
      */
     $this->checkForDuplicate($hash);
     $resourceStatus = $Poster->isOnProbation() ? Schema::PENDING : Schema::POSTED;
     $aData = array(Schema::PRIMARY => $this->Registry->Resource->create('ANSWER'), Schema::QUESTION_ID => $qid, Schema::POSTER_ID => $uid, Schema::QUESTION_OWNER_ID => $this->Question->getOwnerId(), Schema::TITLE => $this->Question->getTitle(), Schema::BODY_HASH => $hash, Schema::POSTER_USERNAME => $username, Schema::USER_PROFILE_URL => '<a href="' . $this->SubmittedAnswer->getUserObject()->getProfileUrl() . '">' . $username . '</a>', Schema::AVATAR_URL => $this->SubmittedAnswer->getUserObject()->getAvatarSrc(), Schema::WORDS_COUNT => $Body->asPlainText()->getWordsCount(), Schema::UPVOTES_COUNT => 0, Schema::DOWNVOTES_COUNT => 0, Schema::VOTES_SCORE => 0, Schema::CATEGORY_ID => $this->Question->getCategoryId(), Schema::BODY => $htmlBody, Schema::CREATED_TIMESTAMP => time(), Schema::LAST_MODIFIED_TIMESTAMP => time(), Schema::TIME_STRING => date('F j, Y g:i a T'), Schema::PLURAL_POSTFIX => 's', Schema::IS_ACCEPTED => false, Schema::IP_ADDRESS => $this->SubmittedAnswer->getIP(), Schema::RESOURCE_STATUS_ID => $resourceStatus, Schema::APP_NAME => 'web');
     if (!empty($aImages)) {
         $aData[Schema::UPLOADED_IMAGES] = $aImages;
     }
     /**
      * Submitted answer object may provide
      * extra elements to be added to aData array
      * This is usually useful for parsing answers that
      * came from external API
      *
      * as well as adding 'credit' div
      */
     $aExtraData = $this->SubmittedAnswer->getExtraData();
     d('$aExtraData: ' . print_r($aExtraData, 1));
     if (!empty($aExtraData)) {
         $aData = array_merge($aData, $aExtraData);
     }
     d('$aData: ' . print_r($aData, 1));
     $this->Answer = new Answer($this->Registry, $aData);
     /**
      * Post onBeforeNewQuestion event
      * and watch for filter either cancelling the event
      * or throwing FilterException (preferred way because
      * a specific error message can be passed in FilterException
      * this way)
      *
      * In either case we throw QuestionParserException
      * Controller that handles the question form should be ready
      * to handle this exception and set the form error using
      * message from exception. This way the error will be shown to
      * the user right on the question form while question form's data
      * is preserved in form.
      */
     try {
         $oNotification = $this->Registry->Dispatcher->post($this->Answer, 'onBeforeNewAnswer');
         if ($oNotification->isNotificationCancelled()) {
             throw new AnswerParserException('@@Sorry, we are unable to process your answer at this time@@');
         }
     } catch (FilterException $e) {
         e('Got filter exception: ' . $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage() . ' ' . $e->getTraceAsString());
         throw new AnswerParserException($e->getMessage());
     }
     /**
      * Do ensureIndexes() now and not before we are sure that we even going
      * to add a new question.
      */
     $this->ensureIndexes();
     $this->Answer->insert();
     d('cp');
     /**
      * post onCategoryUpdate only if status NOT PENDING
      */
     if ($resourceStatus !== Schema::PENDING) {
         $this->updateCategory();
         $this->updateQuestion();
         $this->Registry->Dispatcher->post($this->Question, 'onCategoryUpdate');
         $this->Registry->Dispatcher->post($this->Answer, 'onNewAnswer', array('question' => $this->Question));
     } elseif ($resourceStatus === Schema::PENDING) {
         $this->Registry->Dispatcher->post($this->Answer, 'onNewPendingAnswer');
     }
     d('cp');
     /**
      * Reuse $uid since we already resolved it here,
      * so no need to go through the same
      * $this->SubmittedAnswer->getUserObject()->getUid() again
      */
     $this->addUserTags($uid);
     d('cp');
     return $this;
 }