/** * Post a Link to this question or answer * To User's Facebook Wall * */ protected function post() { try { $reward = \Lampcms\Points::SHARED_CONTENT; $User = $this->Registry->Viewer; $oFB = $this->Registry->Facebook; $Resource = $this->obj; $Mongo = $this->Registry->Mongo; $logo = !empty($this->aFB['SITE_LOGO']) ? $this->aFB['SITE_LOGO'] : null; /** * @todo Translate String(s) of caption * It appears on Facebook Wall under the link * @var string */ $caption = $this->obj instanceof \Lampcms\Question ? 'Please click if you can answer this question' : 'I answered this question'; $description = Utf8String::factory($this->obj['b'], 'utf-8', true)->asPlainText()->valueOf(); } catch (\Exception $e) { d('Unable to post to facebook because of this exception: ' . $e->getMessage() . ' in file: ' . $e->getFile() . ' on line: ' . $e->getLine()); return; } $func = function () use($oFB, $Resource, $User, $reward, $Mongo, $logo, $caption, $description) { $result = null; $aData = array('link' => $Resource->getUrl(), 'name' => $Resource['title'], 'message' => $caption, 'description' => $description); if (!empty($logo) && 'http' == substr($logo, 0, 4)) { $aData['picture'] = $logo; } try { $result = $oFB->postUpdate($User, $aData); } catch (\Exception $e) { // does not matter } if (!empty($result) && false !== ($decoded = json_decode($result, true))) { /** * If status is OK * then reward the user with points! */ if (!empty($decoded['id'])) { $status_id = (string) $decoded['id']; $User->setProfitPoint($reward); /** * Now need to also record Facebook id * to FB_STATUSES collection */ try { /** * Also save fb_status to QUESTIONS or ANSWERS * collection. * This way later on (maybe way later...) * We can add a function so that if user edits * Post on the site we can also edit it * on Tumblr via API * */ $Resource['fb_status'] = $status_id; $Resource->save(); } catch (\Exception $e) { if (function_exists('e')) { e('Unable to save data to FB_STATUSES collection because of ' . $e->getMessage() . ' in file: ' . $e->getFile() . ' on line: ' . $e->getLine()); } } } } }; \Lampcms\runLater($func); }
/** * Send registration email to new user * Email will contain activation link * and instructions to activate the account * * @return Register */ protected function sendActivationEmail() { $Tr = $this->Registry->Tr; $activationLink = $this->makeActivationLink(); $siteName = $this->Registry->Ini->SITE_NAME; $body = $Tr->get('email.body.registration', array('{site_title}' => $siteName, '{username}' => $this->username, '{password}' => $this->pwd, '{link}' => $activationLink)); $body = \Lampcms\Utf8String::leftAlign($body, 2); $subject = $Tr->get('email.subject.registration', array('{site_title}' => $siteName)); /** * By default Mailer::mail sends email from shutdown function (returns immediately, sends later) */ $this->Registry->Mailer->mail($this->email, $subject, $body); return $this; }
/** * Get count of words in this html document * This is the right way to get word count * from HTML doc. The simple way of strip_tags and * then explode by spaces will not work if * html string is just one long * string run together without white spaces * and using regex is usually not the best way * to deal with html string. * * Each Text Node element is then treated * as separate UTF8String object * * This way each text node is split by UTF-8 specific word * delimiters, making it return correct word count * for Any type of language (not only splitting by spaces but * by other accepted delimiters) * * The resulting word count will be accurate for arabic, chinese, * and probably all other languages * * @return int count of words in this html string */ public function getWordsCount() { $count = 0; $Nodes = $this->getTextNodes(); $len = $Nodes->length; if (!$Nodes || 0 === $len) { return 0; } for ($i = 0; $i < $len; $i += 1) { $UTF8String = Utf8String::stringFactory($Nodes->item($i)->data, 'utf-8', true); $count += $UTF8String->getWordsCount(); } return $count; }
/** * * What if email address provided from Facebook * already belongs to some other user? * * This would mean that existing user is just * trying to signup with Facebook. * * In this case we should allow it but ONLY create * a record in the USERS_FACEBOOK table and use users_id * of use that we find by email address * * and then also insert avatar_external into USERS * * @todo create username for user based on Facebook username * Facebook does not really have username, so we can use fn_ln * */ protected function createNewUser() { $extAuth = new \Lampcms\ExternalAuth($this->Registry); d('cp'); $this->Registry->Mongo->USERS->ensureIndex(array('fb_id' => 1)); /** * Time zone offset in seconds * @var int */ $tzo = array_key_exists('timezone', $this->aFbUserData) ? $this->aFbUserData['timezone'] * 3600 : Cookie::get('tzo', 0); /** * User language * @var string */ $lang = !empty($this->aFbUserData['locale']) ? \strtolower(\substr($this->aFbUserData['locale'], 0, 2)) : $this->Registry->getCurrentLang(); /** * User locale * @var string */ $locale = !empty($this->aFbUserData['locale']) ? $this->aFbUserData['locale'] : $this->Registry->Locale->getLocale(); $this->tempPassword = String::makePasswd(); /** * Sid value use existing cookie val * if possible, otherwise create a new one * @var string */ $sid = false === ($sid = Cookie::getSidCookie()) ? String::makeSid() : $sid; $displayName = !empty($this->aFbUserData['name']) ? $this->aFbUserData['name'] : $this->aFbUserData['first_name'] . ' ' . $this->aFbUserData['last_name']; $username = $extAuth->makeUsername($displayName); /** * Create new record in USERS table * do this first because we need uid from * newly created record */ $aUser = array('username' => $username, 'username_lc' => \mb_strtolower($username, 'utf-8'), 'fn' => $this->aFbUserData['first_name'], 'ln' => $this->aFbUserData['last_name'], 'rs' => $sid, 'email' => Utf8String::factory($this->aFbUserData['email'])->toLowerCase()->valueOf(), 'fb_id' => (string) $this->aFbUserData['id'], 'fb_token' => $this->aFbUserData['token'], 'pwd' => String::hashPassword($this->tempPassword), 'avatar_external' => 'http://graph.facebook.com/' . $this->aFbUserData['id'] . '/picture', 'i_reg_ts' => time(), 'date_reg' => date('r'), 'role' => 'external_auth', 'lang' => $lang, 'i_pp' => 1, 'tz' => TimeZone::getTZbyoffset($tzo), 'i_fv' => false !== ($intFv = Cookie::getSidCookie(true)) ? $intFv : time()); if (!empty($this->aFbUserData['gender'])) { $aUser['gender'] = 'male' === $this->aFbUserData['gender'] ? 'M' : 'F'; } $aUser = \array_merge($this->Registry->Geo->Location->data, $aUser); if (!empty($this->aFbUserData['locale'])) { $aUser['locale'] = $this->aFbUserData['locale']; } if (!empty($this->aFbUserData['link'])) { $aUser['fb_url'] = $this->aFbUserData['link']; } d('aUser: '******'$this->User after insert: ' . print_r($this->User->getArrayCopy(), 1)); $this->Registry->Dispatcher->post($this->User, 'onNewUser'); $this->Registry->Dispatcher->post($this->User, 'onNewFacebookUser'); d('cp'); $this->saveEmailAddress(); d('cp'); \Lampcms\PostRegistration::createReferrerRecord($this->Registry, $this->User); return $this; }
/** * (non-PHPdoc) * * @see Lampcms.WebPage::main() */ protected function main() { /** * Do NOT run urldecode() on request string * as it's already decoded because it uses * $_GET as underlying array, and php * already decodes $_GET or $_POST vars */ try { $term = $this->Registry->Router->getSegment(2); $this->term = Utf8String::stringFactory(\urldecode($term)); } catch (\Lampcms\Uri\SegmentNotFoundException $e) { $this->term = $this->Registry->Request->getUTF8('q'); } $this->safeTerm = \str_replace(array('<', '>'), array('<', '>'), $this->term->valueOf()); $this->aPageVars['qheader'] = '<h1>@@Search results for@@: ' . $this->safeTerm . '</h1>'; $this->aPageVars['title'] = '@@Questions matching@@ '' . $this->safeTerm . '''; d('$this->term: ' . $this->term); $this->Search = SearchFactory::get($this->Registry); $this->Search->search($this->term); $this->makeTopTabs()->makeInfo()->makeBody(); }
/** * Find data in Mongo * and create array of $this->aData * * @return object $this */ protected function getData() { $q = $_GET['q']; d('$q: ' . $q); $q = Utf8String::stringFactory($q); $aTokens = TitleTokenizer::factory($q)->getArrayCopy(); if (!empty($aTokens)) { d('looking for something'); try { $cur = $this->Registry->Mongo->QUESTIONS->find(array('a_title' => array('$all' => $aTokens), 'a_deleted' => null), array('_id' => true, 'title' => true, 'url' => true, 'intro' => true, 'hts' => true, 'status' => true, 'i_ans' => true, 'ans_s' => true))->sort(array('status' => 1, 'i_ans' => -1))->limit(12); $this->aData = iterator_to_array($cur, false); d('$this->aData: ' . print_r($this->aData, 1)); } catch (\MongoException $e) { d('MongoException: ' . $e->getMessage() . ' aTokens was: ' . \print_r($this->aTokens, 1)); } } return $this; }
/** * Make new value of title * * @param string $title * * @return object of type Utf8String */ protected function makeTitle($title) { $oTitle = Utf8String::stringFactory($title)->htmlentities()->trim(); d('$oTitle ' . $oTitle); return $oTitle; }
/** * Extract value of tags from * query string and turn into array * runs value of Request through urldecode because * unicode tags would be percent-encoded in the url * * @param string $unasweredSortSection flag indicated that request is for * unanswered tagged items not just tagged items. * If a string is passed it's a value of the COND_TAGGED in the URI_PARTS section in !config.ini * This would mean that * structure of the uri is in the form of {_UNANSWERED_}/{_COND_TAGGED_}/all+tags+here * * @return array of tags passed in query string */ protected function getTags($unasweredSortSection = null) { if (empty($this->aTags)) { $cname = $this->Router->getCalledControllerName(); if ($unasweredSortSection) { $cname .= '/' . $unasweredSortSection; } $cname = \preg_quote($cname, '/'); /** * And now a workaround * for the genocidal RewriteRule bug * that obliterates the urlencoded chars * during the rewrite * so instead we must work directly * with $_SERVER['REQUEST_URI'] * $_SERVER['REQUEST_URI'] is consistently * the same on Apache and on Lighttpd when * php is run as fastcgi * The rewrite on Lighttpd does not have * this genocidal bug, but for consistency * we still working with $_SERVER['REQUEST_URI'] * regardless of the server */ if (!empty($_SERVER) && !empty($_SERVER['REQUEST_URI'])) { /** * Must use regex because REQUEST_URI * may contain also a pageID after the last / * so must extract part from * between /tagged/ and next / * * $r is something like this: /tagged/tag%2B%2B/ */ $r = $_SERVER['REQUEST_URI']; $m = \preg_match('/\\/' . $cname . '\\/([^\\/]+)([\\/]{0,1})/i', $r, $matches); d('matches: ' . \json_encode($matches)); if ($matches && !empty($matches[1])) { $tags = $matches[1]; d('tags: ' . $tags); $this->tags = \urldecode($tags); } } else { /** * That's hopefully is OK * because Apache always has REQUEST_URI * and if it's not available here * then hopefully this is not an Apache server * and it's possible the rewrite worked without this bug */ d('no REQUEST_URI available'); $tags = $this->Request['tags']; $this->tags = \urldecode($tags); } $this->rawTags = $this->tags; /** * Important step to prevent * script or html injection in url GET string * Cannot use htmlspecialchars because we don't want to * also encode the & * */ $this->tags = \str_replace(array('<', '>'), array('<', '>'), $this->tags); $this->title = $this->tags; if (empty($this->tags)) { return array(); } /** * $this->tags are now urldecoded * If this does not work well them try * to use $tags instead */ $Utf8Tags = Utf8String::stringFactory($this->tags); /** * @todo the this->tags now have htmlspecialchars * which may not be what we want in this->aTags * We probably want this->aTags to be raw tags just like * they were submitted in request. * */ $this->aTags = TagsTokenizer::factory($Utf8Tags)->getArrayCopy(); d('aTags: ' . \json_encode($this->aTags)); } return $this->aTags; }
/** * Unlike normal WWW request, * In API call we may not have 'tags' in Request, * so we need to fallback to default empty string here * * (non-PHPdoc) * @see Lampcms.SubmittedQuestionWWW::getUtf8Tags() */ public function getUtf8Tags() { if (!isset($this->Tags)) { $tags = $this->Registry->Request->get('tags', 's', ''); $this->Tags = Utf8String::factory($tags); } return $this->Tags; }
/** * Enforce min and max length of comment * * @todo Translate string * * @throws \Lampcms\Exception */ protected function validateBody(Utf8String $Body) { $len = $Body->length(); if ($len < 10) { /** * @todo * Translate String */ throw new Exception('Ooopsy... Comment must be at least 10 characters long'); } if ($len > 600) { throw new Exception('Oopsy... Comment must be at limited to 600 characters. Your comment is ' . $len . ' characters-long'); } return $this; }
/** * Get value of segment as a Utf8String object * * @param $segmentId * @param null $default * * @return object of type \Lampcms\Utf8String */ public function getUTF8($segmentId, $default = null) { $res = $this->getSegment($segmentId, 's', $default); $ret = Utf8String::stringFactory($res); return $ret; }
/** * @return object of type Utf8string representing * the title string of question */ public function getTitle() { return Utf8String::stringFactory('Quick brown fox'); }
/** * Translate the $item into $locale language * * @param string $locale * @param mixed string|object of type TranslatableInterface $item * * @return string translated string * @throws \Lampcms\DevException */ public function translate($locale, $item) { if (is_string($item)) { return $item; } if (!is_object($item) || !$item instanceof TranslatableInterface) { throw new DevException('$item can only be a string or instance of TranslatableInterface. Was: ' . (!is_object($item) ? gettype($item) : get_class($item))); } $Tr = $this->getTranslator($locale); return \Lampcms\Utf8String::leftAlign($Tr->get($item->getString(), $item->getVars())); }
/** * Prepare the raw string before posting to Twitter * It will convert string to guaranteed utf8, * then strip html tags then truncate to 140 chars * * @param Utf8String $Message * @param string $inReplyToId * * @internal param string $sMessage * @return array of response data from Twitter API */ public function prepareAndPost(Utf8String $Message, $inReplyToId = null) { $body = $Message->htmlspecialchars()->truncate(140)->valueOf(); return $this->postMessage($body, $inReplyToId); }
/** * * 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 * * @return \Lampcms\Question */ public function retag(User $user, array $tags) { parent::offsetSet(Schema::TAGS_ARRAY, $tags); parent::offsetSet('tags_html', \tplQtags::loop($tags, false)); $b = $this->offsetGet(Schema::BODY); d('b: ' . $b); $oHtmlParser = \Lampcms\String\HTMLStringParser::stringFactory(Utf8String::stringFactory($b, 'utf-8', true)); $body = $oHtmlParser->unhilight()->hilightWords($tags)->valueOf(); $this->offsetSet(Schema::BODY, $body); $this->setEdited($user, 'Retagged')->touch(); return $this; }
/** * Enforce min and max length of comment * * * @param Utf8String $Body * * @throws AlertException * @return \Lampcms\CommentParser */ protected function validateBody(Utf8String $Body) { $minChars = $this->Registry->Ini->MIN_COMMENT_CHARS; $maxChars = $this->Registry->Ini->MAX_COMMENT_CHARS; $len = $Body->length(); if ($len < $minChars) { throw new AlertException('@@Comment is too short. Minimal length is@@' . ' ' . $minChars); } if ($len > $maxChars) { throw new AlertException('@@Comment is too long. Maximum number of characters is@@ ' . $maxChars); } return $this; }
/** * Set the URI String * * @param string * * @return string */ public static function setUriString($str) { $str = Utf8String::stripLow($str); self::$uri = $str === '/' ? '' : $str; return self::$uri; }
/** * Removes the tag name from the array of a_f_t * of User object and increases the i_f_t by one * if USER already follow this tag * also decreases the i_flwrs in QUESTION_TAGS collection * by one for this tag * * * @param User $User * @param string $tag * @return \Lampcms\FollowManager * @throws \InvalidArgumentException if $tag is not a string */ public function unfollowTag(User $User, $tag) { if (!is_string($tag)) { throw new \InvalidArgumentException('$tag must be a string'); } $tag = Utf8String::stringFactory($tag)->toLowerCase()->stripTags()->trim()->valueOf(); $aFollowed = $User['a_f_t']; d('$aFollowed: ' . print_r($aFollowed, 1)); if (false !== ($key = array_search($tag, $aFollowed))) { d('cp unsetting key: ' . $key); array_splice($aFollowed, $key, 1); $User['a_f_t'] = $aFollowed; $User->save(); $this->Registry->Mongo->QUESTION_TAGS->update(array('tag' => $tag), array('$inc' => array('i_flwrs' => -1))); $this->Registry->Dispatcher->post($User, 'onTagUnfollow', array('tag' => $tag)); } else { d('tag ' . $tag . ' is not among the followed tags of this userID: ' . $User->getUid()); } return $this; }
/** * Checks in username of twitter user * already exists in our regular USERS table * and if it does then prepends the @ to the username * otherwise returns twitter username * * The result is that we will use the value of * Twitter username as our username OR the * * @username * if username is already taken * * @todo change this to use MONGO USERS and use something like * $any * * @param $displayName * @param bool $isUtf8 * * @return string the value of username that will */ public function makeUsername($displayName, $isUtf8 = false) { d('going to auto_create username based on displayName: ' . $displayName); /** * Make 100% sure that displayName is in UTF8 encoding * Commenting this out for now since it was causing * a problem once. * So for now we going to trust that Facebook give us results * as a valid UTF-8 String */ if (!$isUtf8) { $displayName = Utf8String::stringFactory($displayName)->valueOf(); } $coll = $this->Registry->Mongo->USERS; $res = null; $username = null; $aUsernames = array(preg_replace('/\\s+/', '_', $displayName), preg_replace('/\\s+/', '.', $displayName), preg_replace('/\\s+/', '-', $displayName), preg_replace('/\\s+/', '', $displayName)); $aUsernames = \array_unique($aUsernames); d('$aUsernames: ' . \json_encode($aUsernames, 1)); for ($i = 0; $i < count($aUsernames); $i++) { $name = \mb_strtolower($aUsernames[$i], 'utf-8'); $res = $coll->findOne(array(Schema::USERNAME_LOWERCASE => $name)); d('$res: ' . \var_export($res, 1)); if (empty($res)) { $username = $aUsernames[$i]; break; } } /** * If still could not find username then * use brute force and try appending numbers * to username until succeed */ if (null === $username) { $i = 1; do { $name = \mb_strtolower($aUsernames[0], 'utf-8') . $i; $res = $coll->findOne(array(Schema::USERNAME_LOWERCASE => $name)); if (empty($res)) { $username = $aUsernames[0] . $i; } d('$res: ' . \var_export($res, 1)); $i += 1; } while (null === $username); } return $username; }