/** * @param string $category * @param string|null $subCategory * @throws InvalidArgumentException * @throws UserException */ public function __construct($category, $subCategory = null) { if (!is_string($category)) { throw new InvalidArgumentException('category', 'type_invalid'); } if (!mb_check_encoding($category, 'UTF-8')) { throw new InvalidArgumentException('category', 'encoding_invalid'); } if (!Validator::length(VJ::TAG_MIN, VJ::TAG_MAX)->validate($category)) { throw new UserException('Problem.Tag.invalid_length'); } $keyword = KeywordFilter::isContainGeneric($category); if ($keyword !== false) { throw new UserException('Problem.Tag.name_forbid', ['keyword' => $keyword]); } if (!is_string($subCategory)) { throw new InvalidArgumentException('subCategory', 'type_invalid'); } if (!mb_check_encoding($subCategory, 'UTF-8')) { throw new InvalidArgumentException('subCategory', 'encoding_invalid'); } if (!Validator::length(VJ::TAG_MIN, VJ::TAG_MAX)->validate($subCategory)) { throw new UserException('Problem.Tag.invalid_length'); } $keyword = KeywordFilter::isContainGeneric($subCategory); if ($keyword !== false) { throw new UserException('Problem.Tag.name_forbid', ['keyword' => $keyword]); } $this->category = $category; $this->subCategory = $subCategory; }
public function testSimpleKeywords() { $redis = Application::get('redis'); $filter = new KeywordFilter($redis, $this->prefix); $miss = function () { return ['foo', 'bar', 'baz', 'quux', 'thud', 'grunt']; }; $this->assertEquals($filter->contains('The quick brown foo jumps over the lazy dog', $this->key, $miss), 'foo'); $this->assertEquals($filter->contains('The quick brown fox jumps over the lazy dog', $this->key, $miss), false); $this->assertEquals($filter->contains('The quick brown bar jumps over the lazy baz', $this->key, $miss), 'bar'); $this->assertEquals($filter->contains('The quick brown BAR jumps over the lazy dog', $this->key, $miss), 'bar'); $this->assertEquals($filter->contains('The quick brown FOX jumps over the lazy dog', $this->key, $miss), false); $this->assertEquals($filter->contains('The quick brown quux grunts over the lazy thud', $this->key, $miss), 'quux'); }
/** * @param string $name * @param int|null $year * @throws InvalidArgumentException * @throws UserException */ public function __construct($name, $year = null) { if (!is_string($name)) { throw new InvalidArgumentException('name', 'type_invalid'); } if (!mb_check_encoding($name, 'UTF-8')) { throw new InvalidArgumentException('name', 'encoding_invalid'); } $keyword = KeywordFilter::isContainGeneric($name); if ($keyword !== false) { throw new UserException('Problem.Tag.name_forbid', ['keyword' => $keyword]); } if (!Validator::length(VJ::TAG_MIN, VJ::TAG_MAX)->validate($name)) { throw new UserException('Problem.Tag.invalid_length'); } if ($year !== null) { if (!Validator::int()->validate($year)) { throw new InvalidArgumentException('year', 'type_invalid'); } $year = (int) $year; } $this->name = $name; $this->year = $year; }
/** * 创建用户 * * @param string $username * @param string $password * @param string $email * @return int UID * @throws InvalidArgumentException * @throws UserException */ public function createUser($username, $password, $email) { if (!is_string($username)) { throw new InvalidArgumentException('username', 'type_invalid'); } if (!is_string($password)) { throw new InvalidArgumentException('password', 'type_invalid'); } if (!is_string($email)) { throw new InvalidArgumentException('email', 'type_invalid'); } // 检查用户名 if (!mb_check_encoding($username, 'UTF-8')) { throw new InvalidArgumentException('username', 'encoding_invalid'); } $username = trim($username); if (!Validator::regex('/^\\S*$/')->length(3, 16)->validate($username)) { throw new InvalidArgumentException('username', 'format_invalid'); } // 检查关键字 $keyword = KeywordFilter::isContainGeneric($username); if ($keyword === false) { $keyword = KeywordFilter::isContainName($username); } if ($keyword !== false) { throw new UserException('UserManager.name_forbid', ['keyword' => $keyword]); } // 检查密码 if (!Validator::length(0, 50)->validate($password)) { throw new InvalidArgumentException('password', 'format_invalid'); } // 检查 email if (!Validator::email()->validate($email)) { throw new InvalidArgumentException('password', 'format_invalid'); } // 处理用户名 $username = VJ::removeEmoji($username); // 检查用户名和 Email 是否唯一 if (UserUtil::getUserObjectByUsername($username) !== null) { throw new UserException('UserManager.createUser.user_exists'); } if (UserUtil::getUserObjectByEmail($email) !== null) { throw new UserException('UserManager.createUser.email_exists'); } // 生成 hash & salt $hashSaltPair = $this->user_credential->password_encoder->generateHash($password); // 插入记录 try { $_id = new \MongoId(); $doc = ['_id' => $_id, 'uid' => $_id, 'user' => $username, 'luser' => UserUtil::canonicalizeUsername($username), 'mail' => $email, 'lmail' => UserUtil::canonicalizeEmail($email), 'salt' => $hashSaltPair['salt'], 'hash' => $hashSaltPair['hash'], 'g' => $email, 'gender' => VJ::USER_GENDER_UNKNOWN, 'regat' => new \MongoDate(), 'regip' => $this->request->getClientIp()]; Application::coll('User')->insert($doc); } catch (\MongoCursorException $e) { // 插入失败 throw new UserException('UserManager.createUser.user_or_email_exists'); } // 插入成功:更新 uid // 获取递增 uid $counterRec = Application::coll('System')->findAndModify(['_id' => 'UserCounter'], ['$inc' => ['count' => 1]], [], ['new' => true, 'upsert' => true]); $uid = (int) $counterRec['count']; try { // 修改 uid Application::coll('User')->update(['_id' => $_id], ['$set' => ['uid' => $uid]]); } catch (\MongoCursorException $e) { // 修改 uid 失败(uid 重复),则删除用户记录 Application::critical('createUser.uidDuplicate', ['uid' => $uid]); Application::coll('User')->remove(['_id' => $_id], ['justOne' => true]); throw new UserException('UserManager.createUser.internal'); } Application::emit('user.created', [$uid]); return $uid; }
/** * 修改主题 * * @param \MongoId $discussionId * @param string $markdown * @return array|null * @throws InvalidArgumentException * @throws UserException */ public static function modifyDiscussion(\MongoId $discussionId, $markdown) { if (!is_string($markdown)) { throw new InvalidArgumentException('markdown', 'type_invalid'); } if (!mb_check_encoding($markdown, 'UTF-8')) { throw new InvalidArgumentException('markdown', 'encoding_invalid'); } //if (!Validator::length(VJ::COMMENT_MIN, VJ::COMMENT_MAX)) { //throw new UserException('CommentUtil.content_invalid_length'); //} self::initParser(); $html = self::$parser->parse($markdown); $keyword = KeywordFilter::isContainGeneric(strip_tags($html)); if ($keyword !== false) { throw new UserException('CommentUtil.content_forbid', ['keyword' => $keyword]); } $result = Application::coll('Discussion')->update(['_id' => $discussionId], ['$set' => ['raw' => $markdown, 'html' => $html]]); if ($result['n'] === 1) { Application::emit('discussion.modify.succeeded', [$discussionId]); return ['_id' => $discussionId, 'html' => $html]; } else { return null; } }
/** * 修改回复 * * @param \MongoId $commentId * @param string $ref * @param \MongoId $replyId * @param string $markdown * @return array|null * @throws InvalidArgumentException * @throws UserException */ public static function modifyReply(\MongoId $commentId, $ref, \MongoId $replyId, $markdown) { if (!is_string($ref)) { throw new InvalidArgumentException('ref', 'type_invalid'); } if (!mb_check_encoding($ref, 'UTF-8')) { throw new InvalidArgumentException('ref', 'encoding_invalid'); } if (!is_string($markdown)) { throw new InvalidArgumentException('markdown', 'type_invalid'); } if (!mb_check_encoding($markdown, 'UTF-8')) { throw new InvalidArgumentException('markdown', 'encoding_invalid'); } if (!Validator::length(VJ::COMMENT_MIN, VJ::COMMENT_MAX)) { throw new UserException('CommentUtil.content_invalid_length'); } self::initParser(); $html = self::$parser->parse($markdown); $keyword = KeywordFilter::isContainGeneric(strip_tags($html)); if ($keyword !== false) { throw new UserException('CommentUtil.content_forbid', ['keyword' => $keyword]); } $result = Application::coll('Comment')->update(['_id' => $commentId, 'ref' => $ref, 'deleted' => false, 'replies' => ['$elemMatch' => ['_id' => $replyId, 'deleted' => false]]], ['$set' => ['replies.$.raw' => $markdown, 'replies.$.html' => $html, 'replies.$.modifyat' => new \MongoDate()]]); if ($result['n'] === 1) { Application::emit('comment.reply.modify.succeeded', [$ref, $commentId, $replyId]); return ['_id' => $replyId, 'html' => $html]; } else { return null; } }
public static function modifyMeta(\MongoId $pid, $title, array $tags, array $visibleDomains) { if (!is_string($title)) { throw new InvalidArgumentException('title', 'type_invalid'); } if (!mb_check_encoding($title, 'UTF-8')) { throw new InvalidArgumentException('title', 'encoding_invalid'); } if (!Validator::length(VJ::PROBLEM_TITLE_MIN, VJ::PROBLEM_TITLE_MAX)) { throw new UserException('ProblemUtil.title_invalid_length'); } $keyword = KeywordFilter::isContainGeneric($title); if ($keyword !== false) { throw new UserException('ProblemUtil.title_forbid', ['keyword' => $keyword]); } foreach ($visibleDomains as $domain) { if (!$domain instanceof \MongoId) { throw new InvalidArgumentException('visibleDomains', 'type_invalid'); } } foreach ($tags as $tag) { if (!$tag instanceof Tag) { throw new InvalidArgumentException('tags', 'type_invalid'); } } $title = VJ::removeEmoji($title); $link = self::generateLink($title); try { $result = Application::coll('Problem')->update(['_id' => $pid], ['$set' => ['title' => $title, 'link' => $link, 'llink' => self::canonicalizeLink($link), 'tags' => array_map(function (Tag $tag) { return $tag->serializeForDb(); }, $tags), 'visible' => $visibleDomains]]); } catch (\MongoCursorException $e) { throw new UserException('ProblemUtil.modifyMeta.title_exists'); } return $result['n'] === 1; }