db->fetchAll($this->db ->select()->from('table.metas') ->join('table.relationships', 'table.relationships.mid = table.metas.mid') ->where('table.relationships.cid = ?', $this->cid) ->where('table.metas.type = ?', 'tag'), array($this->widget('Widget_Abstract_Metas'), 'filter')); } /** * 文章作者 * * @access protected * @return Typecho_Config */ protected function ___author() { return $this->widget('Widget_Users_Author@' . $this->cid, array('uid' => $this->authorId)); } /** * 获取词义化日期 * * @access protected * @return string */ protected function ___dateWord() { return $this->date->word(); } /** * 获取父id * * @access protected * @return string */ protected function ___parentId() { return $this->row['parent']; } /** * 对文章的简短纯文本描述 * * @access protected * @return string */ protected function ___description() { $plainTxt = str_replace("\n", '', trim(strip_tags($this->excerpt))); $plainTxt = $plainTxt ? $plainTxt : $this->title; return Typecho_Common::subStr($plainTxt, 0, 100, '...'); } /** * ___fields * * @access protected * @return Typecho_Config */ protected function ___fields() { $fields = array(); $rows = $this->db->fetchAll($this->db->select()->from('table.fields') ->where('cid = ?', $this->cid)); foreach ($rows as $row) { $fields[$row['name']] = $row[$row['type'] . '_value']; } return new Typecho_Config($fields); } /** * 获取文章内容摘要 * * @access protected * @return string */ protected function ___excerpt() { if ($this->hidden) { return $this->text; } $content = $this->pluginHandle(__CLASS__)->trigger($plugged)->excerpt($this->text, $this); if (!$plugged) { $content = $this->isMarkdown ? $this->markdown($content) : $this->autoP($content); } $contents = explode('', $content); list($excerpt) = $contents; return Typecho_Common::fixHtml($this->pluginHandle(__CLASS__)->excerptEx($excerpt, $this)); } /** * 获取文章内容 * * @access protected * @return string */ protected function ___content() { if ($this->hidden) { return $this->text; } $content = $this->pluginHandle(__CLASS__)->trigger($plugged)->content($this->text, $this); if (!$plugged) { $content = $this->isMarkdown ? $this->markdown($content) : $this->autoP($content); } return $this->pluginHandle(__CLASS__)->contentEx($content, $this); } /** * 输出文章的第一行作为摘要 * * @return string */ protected function ___summary() { $content = $this->content; $parts = preg_split("/(<\/\s*(?:p|blockquote|q|pre|table)\s*>)/i", $content, 2, PREG_SPLIT_DELIM_CAPTURE); if (!empty($parts)) { $content = $parts[0] . $parts[1]; } return $content; } /** * 锚点id * * @access protected * @return string */ protected function ___theId() { return $this->type . '-' . $this->cid; } /** * 回复框id * * @access protected * @return string */ protected function ___respondId() { return 'respond-' . $this->theId; } /** * 评论地址 * * @access protected * @return string */ protected function ___commentUrl() { /** 生成反馈地址 */ /** 评论 */ return Typecho_Router::url('feedback', array('type' => 'comment', 'permalink' => $this->pathinfo), $this->options->index); } /** * trackback地址 * * @access protected * @return string */ protected function ___trackbackUrl() { return Typecho_Router::url('feedback', array('type' => 'trackback', 'permalink' => $this->pathinfo), $this->options->index); } /** * 回复地址 * * @access protected * @return string */ protected function ___responseUrl() { return $this->permalink . '#' . $this->respondId; } /** * 获取页面偏移 * * @access protected * @param string $column 字段名 * @param integer $offset 偏移值 * @param string $type 类型 * @param string $status 状态值 * @param integer $authorId 作者 * @param integer $pageSize 分页值 * @return integer */ protected function getPageOffset($column, $offset, $type, $status = NULL, $authorId = 0, $pageSize = 20) { $select = $this->db->select(array('COUNT(table.contents.cid)' => 'num'))->from('table.contents') ->where("table.contents.{$column} > {$offset}") ->where("table.contents.type = ?", $type); if (!empty($status)) { $select->where("table.contents.status = ?", $status); } if ($authorId > 0) { $select->where('table.contents.authorId = ?', $authorId); } $count = $this->db->fetchObject($select)->num + 1; return ceil($count / $pageSize); } /** * 获取查询对象 * * @access public * @return Typecho_Db_Query */ public function select() { return $this->db->select('table.contents.cid', 'table.contents.title', 'table.contents.slug', 'table.contents.created', 'table.contents.authorId', 'table.contents.modified', 'table.contents.type', 'table.contents.status', 'table.contents.text', 'table.contents.commentsNum', 'table.contents.order', 'table.contents.template', 'table.contents.password', 'table.contents.allowComment', 'table.contents.allowPing', 'table.contents.allowFeed', 'table.contents.parent')->from('table.contents'); } /** * 插入内容 * * @access public * @param array $content 内容数组 * @return integer */ public function insert(array $content) { /** 构建插入结构 */ $insertStruct = array( 'title' => empty($content['title']) ? NULL : htmlspecialchars($content['title']), 'created' => empty($content['created']) ? $this->options->gmtTime : $content['created'], 'modified' => $this->options->gmtTime, 'text' => empty($content['text']) ? NULL : $content['text'], 'order' => empty($content['order']) ? 0 : intval($content['order']), 'authorId' => isset($content['authorId']) ? $content['authorId'] : $this->user->uid, 'template' => empty($content['template']) ? NULL : $content['template'], 'type' => empty($content['type']) ? 'post' : $content['type'], 'status' => empty($content['status']) ? 'publish' : $content['status'], 'password' => empty($content['password']) ? NULL : $content['password'], 'commentsNum' => empty($content['commentsNum']) ? 0 : $content['commentsNum'], 'allowComment' => !empty($content['allowComment']) && 1 == $content['allowComment'] ? 1 : 0, 'allowPing' => !empty($content['allowPing']) && 1 == $content['allowPing'] ? 1 : 0, 'allowFeed' => !empty($content['allowFeed']) && 1 == $content['allowFeed'] ? 1 : 0, 'parent' => empty($content['parent']) ? 0 : intval($content['parent']) ); if (!empty($content['cid'])) { $insertStruct['cid'] = $content['cid']; } /** 首先插入部分数据 */ $insertId = $this->db->query($this->db->insert('table.contents')->rows($insertStruct)); /** 更新缩略名 */ if ($insertId > 0) { $this->applySlug(empty($content['slug']) ? NULL : $content['slug'], $insertId); } return $insertId; } /** * 更新内容 * * @access public * @param array $content 内容数组 * @param Typecho_Db_Query $condition 更新条件 * @return integer */ public function update(array $content, Typecho_Db_Query $condition) { /** 首先验证写入权限 */ if (!$this->isWriteable(clone $condition)) { return false; } /** 构建更新结构 */ $preUpdateStruct = array( 'title' => empty($content['title']) ? NULL : htmlspecialchars($content['title']), 'order' => empty($content['order']) ? 0 : intval($content['order']), 'text' => empty($content['text']) ? NULL : $content['text'], 'template' => empty($content['template']) ? NULL : $content['template'], 'type' => empty($content['type']) ? 'post' : $content['type'], 'status' => empty($content['status']) ? 'publish' : $content['status'], 'password' => empty($content['password']) ? NULL : $content['password'], 'allowComment' => !empty($content['allowComment']) && 1 == $content['allowComment'] ? 1 : 0, 'allowPing' => !empty($content['allowPing']) && 1 == $content['allowPing'] ? 1 : 0, 'allowFeed' => !empty($content['allowFeed']) && 1 == $content['allowFeed'] ? 1 : 0, 'parent' => empty($content['parent']) ? 0 : intval($content['parent']) ); $updateStruct = array(); foreach ($content as $key => $val) { if (array_key_exists($key, $preUpdateStruct)) { $updateStruct[$key] = $preUpdateStruct[$key]; } } /** 更新创建时间 */ if (!empty($content['created'])) { $updateStruct['created'] = $content['created']; } $updateStruct['modified'] = $this->options->gmtTime; /** 首先插入部分数据 */ $updateCondition = clone $condition; $updateRows = $this->db->query($condition->update('table.contents')->rows($updateStruct)); /** 更新缩略名 */ if ($updateRows > 0 && isset($content['slug'])) { $this->applySlug(empty($content['slug']) ? NULL : $content['slug'], $updateCondition); } return $updateRows; } /** * 删除内容 * * @access public * @param Typecho_Db_Query $condition 查询对象 * @return integer */ public function delete(Typecho_Db_Query $condition) { return $this->db->query($condition->delete('table.contents')); } /** * 为内容应用缩略名 * * @access public * @param string $slug 缩略名 * @param mixed $cid 内容id * @return string */ public function applySlug($slug, $cid) { if ($cid instanceof Typecho_Db_Query) { $cid = $this->db->fetchObject($cid->select('cid') ->from('table.contents')->limit(1))->cid; } /** 生成一个非空的缩略名 */ $slug = Typecho_Common::slugName($slug, $cid); $result = $slug; /** 对草稿的slug做特殊处理 */ $draft = $this->db->fetchObject($this->db->select('type', 'parent') ->from('table.contents')->where('cid = ?', $cid)); if ('_draft' == substr($draft->type, -6) && $draft->parent) { $result = '@' . $result; } /** 判断是否在数据库中已经存在 */ $count = 1; while ($this->db->fetchObject($this->db->select(array('COUNT(cid)' => 'num')) ->from('table.contents')->where('slug = ? AND cid <> ?', $result, $cid))->num > 0) { $result = $slug . '-' . $count; $count ++; } $this->db->query($this->db->update('table.contents')->rows(array('slug' => $result)) ->where('cid = ?', $cid)); return $result; } /** * 删除自定义字段 * * @param integer $cid * @access public * @return integer */ public function deleteFields($cid) { return $this->db->query($this->db->delete('table.fields') ->where('cid = ?', $cid)); } /** * 检查字段名是否符合要求 * * @param string $name * @access public * @return boolean */ public function checkFieldName($name) { return preg_match("/^[_a-z][_a-z0-9]*$/i", $name); } /** * 保存自定义字段 * * @param array $fields * @param mixed $cid * @access public * @return void */ public function applyFields(array $fields, $cid) { $exists = array_flip(Typecho_Common::arrayFlatten($this->db->fetchAll($this->db->select('name') ->from('table.fields')->where('cid = ?', $cid)), 'name')); foreach ($fields as $name => $value) { $type = 'str'; if (is_array($value) && 2 == count($value)) { $type = $value[0]; $value = $value[1]; } else if (strpos($name, ':') > 0) { list ($type, $name) = explode(':', $name, 2); } if (!$this->checkFieldName($name)) { continue; } $isFieldReadOnly = $this->pluginHandle(__CLASS__)->trigger($plugged)->isFieldReadOnly($name); if ($plugged && $isFieldReadOnly) { continue; } if (isset($exists[$name])) { unset($exists[$name]); } $this->setField($name, $type, $value, $cid); } foreach ($exists as $name => $value) { $this->db->query($this->db->delete('table.fields') ->where('cid = ? AND name = ?', $cid, $name)); } } /** * 设置单个字段 * * @param string $name * @param string $type * @param string $value * @param integer $cid * @access public * @return integer */ public function setField($name, $type, $value, $cid) { if (empty($name) || !$this->checkFieldName($name) || !in_array($type, array('str', 'int', 'float'))) { return false; } $exist = $this->db->fetchRow($this->db->select('cid')->from('table.fields') ->where('cid = ? AND name = ?', $cid, $name)); if (empty($exist)) { return $this->db->query($this->db->insert('table.fields') ->rows(array( 'cid' => $cid, 'name' => $name, 'type' => $type, 'str_value' => 'str' == $type ? $value : NULL, 'int_value' => 'int' == $type ? intval($value) : 0, 'float_value' => 'float' == $type ? floatval($value) : 0 ))); } else { return $this->db->query($this->db->update('table.fields') ->rows(array( 'type' => $type, 'str_value' => 'str' == $type ? $value : NULL, 'int_value' => 'int' == $type ? intval($value) : 0, 'float_value' => 'float' == $type ? floatval($value) : 0 )) ->where('cid = ? AND name = ?', $cid, $name)); } } /** * 自增一个整形字段 * * @param string $name * @param integer $value * @param integer $cid * @access public * @return integer */ public function incrIntField($name, $value, $cid) { if (!$this->checkFieldName($name)) { return false; } $exist = $this->db->fetchRow($this->db->select('type')->from('table.fields') ->where('cid = ? AND name = ?', $cid, $name)); $value = intval($value); if (empty($exist)) { return $this->db->query($this->db->insert('table.fields') ->rows(array( 'cid' => $cid, 'name' => $name, 'type' => 'int', 'str_value' => NULL, 'int_value' => $value, 'float_value' => 0 ))); } else { $struct = array( 'str_value' => NULL, 'float_value' => NULL ); if ('int' != $exist['type']) { $struct['type'] = 'int'; } return $this->db->query($this->db->update('table.fields') ->rows($struct) ->expression('int_value', 'int_value ' . ($value >= 0 ? '+' : '') . $value) ->where('cid = ? AND name = ?', $cid, $name)); } } /** * 内容是否可以被修改 * * @access public * @param Typecho_Db_Query $condition 条件 * @return mixed */ public function isWriteable(Typecho_Db_Query $condition) { $post = $this->db->fetchRow($condition->select('authorId')->from('table.contents')->limit(1)); return $post && ($this->user->pass('editor', true) || $post['authorId'] == $this->user->uid); } /** * 按照条件计算内容数量 * * @access public * @param Typecho_Db_Query $condition 查询对象 * @return integer */ public function size(Typecho_Db_Query $condition) { return $this->db->fetchObject($condition ->select(array('COUNT(DISTINCT table.contents.cid)' => 'num')) ->from('table.contents') ->cleanAttribute('group'))->num; } /** * 获取当前所有自定义模板 * * @access public * @return array */ public function getTemplates() { $files = glob($this->options->themeFile($this->options->theme, '*.php')); $result = array(); foreach ($files as $file) { $info = Typecho_Plugin::parseInfo($file); $file = basename($file); if ('index.php' != $file && 'custom' == $info['title']) { $result[$file] = $info['description']; } } return $result; } /** * 通用过滤器 * * @access public * @param array $value 需要过滤的行数据 * @return array * @throws Typecho_Widget_Exception */ public function filter(array $value) { /** 取出所有分类 */ $value['categories'] = $this->db->fetchAll($this->db ->select()->from('table.metas') ->join('table.relationships', 'table.relationships.mid = table.metas.mid') ->where('table.relationships.cid = ?', $value['cid']) ->where('table.metas.type = ?', 'category') ->order('table.metas.order', Typecho_Db::SORT_ASC), array($this->widget('Widget_Metas_Category_List'), 'filter')); $value['category'] = NULL; $value['directory'] = array(); /** 取出第一个分类作为slug条件 */ if (!empty($value['categories'])) { $value['category'] = $value['categories'][0]['slug']; $value['directory'] = $this->widget('Widget_Metas_Category_List')->getAllParents($value['categories'][0]['mid']); $value['directory'][] = $value['category']; } $value['date'] = new Typecho_Date($value['created']); /** 生成日期 */ $value['year'] = $value['date']->year; $value['month'] = $value['date']->month; $value['day'] = $value['date']->day; /** 生成访问权限 */ $value['hidden'] = false; /** 获取路由类型并判断此类型在路由表中是否存在 */ $type = $value['type']; $routeExists = (NULL != Typecho_Router::get($type)); $tmpSlug = $value['slug']; $tmpCategory = $value['category']; $tmpDirectory = $value['directory']; $value['slug'] = urlencode($value['slug']); $value['category'] = urlencode($value['category']); $value['directory'] = implode('/', array_map('urlencode', $value['directory'])); /** 生成静态路径 */ $value['pathinfo'] = $routeExists ? Typecho_Router::url($type, $value) : '#'; /** 生成静态链接 */ $value['permalink'] = Typecho_Common::url($value['pathinfo'], $this->options->index); /** 处理附件 */ if ('attachment' == $type) { $content = @unserialize($value['text']); //增加数据信息 $value['attachment'] = new Typecho_Config($content); $value['attachment']->isImage = in_array($content['type'], array('jpg', 'jpeg', 'gif', 'png', 'tiff', 'bmp')); $value['attachment']->url = Widget_Upload::attachmentHandle($value); if ($value['attachment']->isImage) { $value['text'] = '' .
                $value['title'] . ''; } else { $value['text'] = '' . $value['title'] . ''; } } /** 处理Markdown **/ if (isset($value['text'])) { $value['isMarkdown'] = (0 === strpos($value['text'], '')); if ($value['isMarkdown']) { $value['text'] = substr($value['text'], 15); } } /** 生成聚合链接 */ /** RSS 2.0 */ $value['feedUrl'] = $routeExists ? Typecho_Router::url($type, $value, $this->options->feedUrl) : '#'; /** RSS 1.0 */ $value['feedRssUrl'] = $routeExists ? Typecho_Router::url($type, $value, $this->options->feedRssUrl) : '#'; /** ATOM 1.0 */ $value['feedAtomUrl'] = $routeExists ? Typecho_Router::url($type, $value, $this->options->feedAtomUrl) : '#'; $value['slug'] = $tmpSlug; $value['category'] = $tmpCategory; $value['directory'] = $tmpDirectory; /** 处理密码保护流程 */ if (!empty($value['password']) && $value['password'] !== Typecho_Cookie::get('protectPassword') && $value['authorId'] != $this->user->uid && !$this->user->pass('editor', true)) { $value['hidden'] = true; /** 抛出错误 */ if ($this->request->isPost() && isset($this->request->protectPassword)) { throw new Typecho_Widget_Exception(_t('对不起,您输入的密码错误'), 403); } } $value = $this->pluginHandle(__CLASS__)->filter($value, $this); /** 如果访问权限被禁止 */ if ($value['hidden']) { $value['text'] = '
' . '

' . _t('请输入密码访问') . '

' . '

' . '
'; $value['title'] = _t('此内容被密码保护'); $value['tags'] = array(); $value['commentsNum'] = 0; } return $value; } /** * 将每行的值压入堆栈 * * @access public * @param array $value 每行的值 * @return array */ public function push(array $value) { $value = $this->filter($value); return parent::push($value); } /** * 输出文章发布日期 * * @access public * @param string $format 日期格式 */ public function date($format = NULL) { echo $this->date->format(empty($format) ? $this->options->postDateFormat : $format); } /** * 输出文章内容 * * @access public * @param mixed $more 文章截取后缀 */ public function content($more = false) { echo false !== $more && false !== strpos($this->text, '') ? $this->excerpt . "

permalink}\" title=\"{$this->title}\">{$more}

" : $this->content; } /** * 输出文章摘要 * * @access public * @param integer $length 摘要截取长度 * @param string $trim 摘要后缀 */ public function excerpt($length = 100, $trim = '...') { echo Typecho_Common::subStr(strip_tags($this->excerpt), 0, $length, $trim); } /** * 输出标题 * * @access public * @param integer $length 标题截取长度 * @param string $trim 截取后缀 */ public function title($length = 0, $trim = '...') { $title = $this->pluginHandle()->trigger($plugged)->title($this->title, $this); if (!$plugged) { echo $length > 0 ? Typecho_Common::subStr($this->title, 0, $length, $trim) : $this->title; } else { echo $title; } } /** * 输出文章评论数 * * @access public */ public function commentsNum() { $args = func_get_args(); if (!$args) { $args[] = '%d'; } $num = intval($this->commentsNum); echo sprintf(isset($args[$num]) ? $args[$num] : array_pop($args), $num); } /** * 获取文章权限 * * @access public */ public function allow() { $permissions = func_get_args(); $allow = true; foreach ($permissions as $permission) { $permission = strtolower($permission); if ('edit' == $permission) { $allow &= ($this->user->pass('editor', true) || $this->authorId == $this->user->uid); } else { /** 对自动关闭反馈功能的支持 */ if (('ping' == $permission || 'comment' == $permission) && $this->options->commentsPostTimeout > 0 && $this->options->commentsAutoClose) { if ($this->options->gmtTime - $this->created > $this->options->commentsPostTimeout) { return false; } } $allow &= ($this->row['allow' . ucfirst($permission)] == 1) and !$this->hidden; } } return $allow; } /** * 输出文章分类 * * @access public * @param string $split 多个分类之间分隔符 * @param boolean $link 是否输出链接 * @param string $default 如果没有则输出 * @return void */ public function category($split = ',', $link = true, $default = NULL) { $categories = $this->categories; if ($categories) { $result = array(); foreach ($categories as $category) { $result[] = $link ? '' . $category['name'] . '' : $category['name']; } echo implode($split, $result); } else { echo $default; } } /** * 输出文章标签 * * @access public * @param string $split 多个标签之间分隔符 * @param boolean $link 是否输出链接 * @param string $default 如果没有则输出 * @return void */ public function tags($split = ',', $link = true, $default = NULL) { /** 取出tags */ if ($this->tags) { $result = array(); foreach ($this->tags as $tag) { $result[] = $link ? '' . $tag['name'] . '' : $tag['name']; } echo implode($split, $result); } else { echo $default; } } /** * 输出当前作者 * * @access public * @param string $item 需要输出的项目 * @return void */ public function author($item = 'screenName') { echo $this->author->{$item}; } /** * autoP * * @param mixed $text * @access public * @return void */ public function autoP($text) { $html = $this->pluginHandle(__CLASS__)->trigger($parsed)->autoP($text); if (!$parsed) { static $parser; if (empty($parser)) { $parser = new AutoP(); } $html = $parser->parse($text); } return $html; } /** * markdown * * @param mixed $text * @access public * @return void */ public function markdown($text) { $html = $this->pluginHandle(__CLASS__)->trigger($parsed)->markdown($text); if (!$parsed) { $html = Markdown::convert($text); } return $html; } }